From f7b59e7e14556700d27b7a0ca1c2212bdd1759f0 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 27 Jul 2026 02:55:56 -0700 Subject: [PATCH] test: close coverage gaps found in final branch review - add test_rebuild_inherits_the_parents_cover: covers the cover_path inheritance mechanism in the rebuild endpoint (Task 2's upload-retention design relied on it, but nothing exercised it) - tighten test_lineage_marker_survives_in_the_accessibility_tree to assert the literal " rebuilt" screen-reader text, not just that rebuilt_from is referenced somewhere in paintCard - normalize submit's youtube_url/genre/language the same way rebuild does, for symmetry, plus a pinning test - pin acquire's stage outputs to [meta.json, audio.wav] in test_acquire.py, since web.py's _require_source hardcodes those same filenames --- src/karaoke_maker/web.py | 2 ++ tests/test_acquire.py | 6 ++++++ tests/test_web.py | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/karaoke_maker/web.py b/src/karaoke_maker/web.py index acdfc13..e9e0c86 100644 --- a/src/karaoke_maker/web.py +++ b/src/karaoke_maker/web.py @@ -194,6 +194,8 @@ def create_app(cfg: Config, runner=run_pipeline) -> FastAPI: language: str | None = Form(None), bpm: float | None = Form(None), video: UploadFile | None = File(None), cover: UploadFile | None = File(None)): + youtube_url, genre, language = (youtube_url or None, genre or None, + language or None) has_video = bool(video and video.filename) if bool(youtube_url) == has_video: raise HTTPException(400, "provide exactly one of youtube_url / video") diff --git a/tests/test_acquire.py b/tests/test_acquire.py index 8619deb..dca4d48 100644 --- a/tests/test_acquire.py +++ b/tests/test_acquire.py @@ -38,6 +38,12 @@ def test_youtube(tmp_path, monkeypatch): assert meta["video_file"] == "source_video.webm" assert meta["thumbnail"] == "source_video.jpg" +def test_outputs_are_meta_and_audio(tmp_path): + """_require_source in web.py hardcodes these filenames; pin them here.""" + ctx = make_ctx(tmp_path, youtube_url="https://yt/x") + outputs = acquire.acquire_stage().outputs(ctx) + assert [p.name for p in outputs] == ["meta.json", "audio.wav"] + def test_requires_exactly_one_source(tmp_path): import pytest from karaoke_maker.pipeline import PipelineError diff --git a/tests/test_web.py b/tests/test_web.py index 27931ee..d4b0c19 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -52,6 +52,16 @@ def test_failure_reported(tmp_path): assert job["error"] == "boom" +def test_submit_normalises_a_blank_genre_to_none(tmp_path): + seen = [] + client, _ = make_client(tmp_path, capture_runner_factory(seen)) + resp = client.post("/api/jobs", data={ + "title": "T", "artist": "A", "lyrics": "x", + "youtube_url": "http://y", "genre": ""}) + wait_status(client, resp.json()["id"], "done") + assert seen[0]["inp"].genre is None + + def test_requires_one_source(tmp_path): client, _ = make_client(tmp_path, ok_runner) resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x"}) @@ -599,6 +609,7 @@ def test_lineage_marker_survives_in_the_accessibility_tree(): html = _index_html() body = _fn_body(html, "paintCard") assert "rebuilt_from" in body + assert '" rebuilt"' in body assert '"sr-only"' in _fn_body(html, "jobCard") @@ -815,6 +826,20 @@ def test_rebuild_inherits_the_parents_source(tmp_path): assert seen[1]["inp"].video_path == seen[0]["inp"].video_path +def test_rebuild_inherits_the_parents_cover(tmp_path): + seen = [] + client, _ = make_client(tmp_path, capture_runner_factory(seen)) + resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x", + "youtube_url": "http://y"}, + files={"cover": ("c.jpg", io.BytesIO(b"img"), "image/jpeg")}) + parent = resp.json()["id"] + wait_status(client, parent, "done") + child = client.post(f"/api/jobs/{parent}/rebuild", + data=rebuild_form(year="1998")).json()["id"] + wait_status(client, child, "done") + assert seen[1]["inp"].cover_path == seen[0]["inp"].cover_path + + def test_rebuild_rejects_a_missing_upload_that_would_be_needed(tmp_path): client, _ = make_client(tmp_path, ok_runner) resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x"},