aboutsummaryrefslogtreecommitdiff
path: root/lib/project/tests/test_gui.py
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-09-12 21:18:34 -0400
committerhistoria <historiavg@proton.me>2026-09-12 21:18:34 -0400
commit544134f0534d2e4e4fd7ab823d8a0da543fa80ef (patch)
treeef0f787b6377608b3814ac642804e8c778980ddf /lib/project/tests/test_gui.py
parent0cd53b67793e74e8be0201b90711cb51b7186d6e (diff)
downloadproducer-544134f0534d2e4e4fd7ab823d8a0da543fa80ef.tar.gz
fix: output wavs from gui go into correct directory
Diffstat (limited to 'lib/project/tests/test_gui.py')
-rw-r--r--lib/project/tests/test_gui.py87
1 files changed, 69 insertions, 18 deletions
diff --git a/lib/project/tests/test_gui.py b/lib/project/tests/test_gui.py
index 2f17549..83a8220 100644
--- a/lib/project/tests/test_gui.py
+++ b/lib/project/tests/test_gui.py
@@ -13,7 +13,7 @@ import soundfile as sf
pytest.importorskip("fastapi")
from fastapi.testclient import TestClient
-from producer.config import Settings
+from producer.config import Settings, resolve
from producer import gui as gui_module
from producer.gui import create_app
from producer import gui_jobs
@@ -24,20 +24,21 @@ URL = "http://127.0.0.1:8765"
@pytest.fixture
def client(tmp_path):
with TestClient(create_app(8765, output_dir=tmp_path), base_url=URL) as client:
+ # Per-server upload cache; module-global caches would leak stale
+ # upload ids across tests (id() of dead Path objects gets reused).
+ client._upload_cache = {}
yield client
-_UPLOADED = {}
-
-
def upload(client, path, name=None):
"""Upload once per (file, name) and reuse the entry, like the browser UI."""
- key = (id(path), name)
- if key not in _UPLOADED:
+ cache = client._upload_cache
+ key = (str(path), name)
+ if key not in cache:
response = client.post("/api/uploads", params={"name": name or path.name}, content=path.read_bytes())
assert response.status_code == 200, response.text
- _UPLOADED[key] = response.json()
- return _UPLOADED[key]
+ cache[key] = response.json()
+ return cache[key]
def wait(client, job_id):
@@ -200,16 +201,20 @@ def test_real_preview_context_exact_crop_bypass_and_export_trim(client, tmp_path
assert repeated["cached"] and repeated["result"]["result_id"] == result["result_id"]
changed = wait(client, submit(client, source, "preview", settings=requested | {"highpass": True}, start=2, duration=3)["id"])
assert not changed["cached"] and changed["result"]["result_id"] != result["result_id"]
- for _ in range(2):
- exported = wait(client, submit(client, source, "export", settings=requested,
- output_dir=str(tmp_path))["id"])
- assert exported["status"] == "succeeded", exported
- assert exported["result"]["settings"] == requested
- assert len(exported["result"]["paths"]) == 3
- assert "not auditioned" in exported["result"]["notice"]
- for path in exported["result"]["paths"]:
- assert Path(path).is_absolute() and Path(path).is_file()
- assert len(list(tmp_path.glob("producer-export-*"))) == 2
+ exported = wait(client, submit(client, source, "export", settings=requested,
+ output_dir=str(tmp_path))["id"])
+ assert exported["status"] == "succeeded", exported
+ assert exported["result"]["settings"] == requested
+ assert len(exported["result"]["paths"]) == 3
+ assert "not auditioned" in exported["result"]["notice"]
+ # Audio directly in the output directory; the timestamped report in
+ # the shared logs directory. No producer-export-* folders anywhere.
+ for path in exported["result"]["paths"]:
+ assert Path(path).is_absolute() and Path(path).is_file()
+ assert Path(exported["result"]["paths"][0]) == tmp_path / "input.custom.wav"
+ assert Path(exported["result"]["paths"][1]) == tmp_path / "input.custom.mp3"
+ report = Path(exported["result"]["paths"][2])
+ assert report.parent.name == "logs" and report.name.startswith("input.custom.")
assert hashlib.sha256(source.read_bytes()).hexdigest() == before
@@ -221,6 +226,12 @@ def test_export_output_directory_rules(client, wav, bypass, tmp_path):
"settings": requested}
assert client.post("/api/jobs", json={**base, "output_dir": "relative/folder"}).status_code == 400
assert client.post("/api/jobs", json={**base, "output_dir": str(tmp_path / "missing")}).status_code == 400
+ assert client.post("/api/jobs", json={**base, "overwrite": "yes"}).status_code == 400
+ assert client.post("/api/export/check",
+ json={**base, "input_id": "stale"}).status_code == 400
+ assert client.post("/api/export/check",
+ json={"upload_id": entry["upload_id"], "input_id": entry["input_id"],
+ "settings": requested, "output_dir": "relative"}).status_code == 400
missing = client.post("/api/jobs", json={**base})
assert missing.status_code == 202
job = wait(client, missing.json()["id"])
@@ -229,6 +240,46 @@ def test_export_output_directory_rules(client, wav, bypass, tmp_path):
assert Path(path).is_absolute() and Path(path).is_file()
Path(path).relative_to(tmp_path)
+
+def test_export_check_exists_and_overwrite_replaces_audio(client, wav, bypass, tmp_path, logs):
+ source = wav()
+ entry = upload(client, source)
+ requested = asdict(bypass)
+ body = {"upload_id": entry["upload_id"], "input_id": entry["input_id"],
+ "settings": requested, "output_dir": str(tmp_path)}
+ check = client.post("/api/export/check", json=body)
+ assert check.status_code == 200
+ assert check.json() == {"exists": False, "paths": [str(tmp_path / "input.custom.wav")]}
+ first = wait(client, submit(client, source, "export", settings=requested,
+ output_dir=str(tmp_path))["id"])
+ assert first["status"] == "succeeded", first
+ assert first["result"]["paths"][0] == str(tmp_path / "input.custom.wav")
+ assert client.post("/api/export/check", json=body).json() == {
+ "exists": True, "paths": [str(tmp_path / "input.custom.wav")]}
+ # Without confirmation the export refuses to replace the audio; with the
+ # browser-confirmed overwrite flag it replaces it.
+ refused = wait(client, submit(client, source, "export", settings=requested,
+ output_dir=str(tmp_path))["id"])
+ assert refused["status"] == "failed" and "Output exists" in refused["error"]
+ replaced = wait(client, submit(client, source, "export", settings=requested,
+ output_dir=str(tmp_path), overwrite=True)["id"])
+ assert replaced["status"] == "succeeded", replaced
+ assert [path for path in tmp_path.glob("input.custom.*")] == [tmp_path / "input.custom.wav"]
+ # Reports are timestamped, so both exports left one report each.
+ assert len(list(logs.glob("input.custom.*.report.json"))) == 2
+
+
+def test_export_name_matches_pure_builtin_profile(client, wav, tmp_path):
+ # Naming comes from the server: settings exactly equal to a built-in
+ # profile keep its name; anything else is 'custom' (no processing needed).
+ source = wav()
+ entry = upload(client, source)
+ pure = asdict(resolve(profile="natural"))
+ body = {"upload_id": entry["upload_id"], "input_id": entry["input_id"],
+ "settings": pure, "output_dir": str(tmp_path)}
+ check = client.post("/api/export/check", json=body)
+ assert check.json() == {"exists": False, "paths": [str(tmp_path / "input.natural.wav")]}
+
def test_preview_bounds_and_empty_inputs(client, wav):
source = wav()
entry = upload(client, source)