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
This commit is contained in:
2026-07-27 02:55:56 -07:00
parent 9a5ef49ab6
commit f7b59e7e14
3 changed files with 33 additions and 0 deletions
+2
View File
@@ -194,6 +194,8 @@ def create_app(cfg: Config, runner=run_pipeline) -> FastAPI:
language: str | None = Form(None), bpm: float | None = Form(None), language: str | None = Form(None), bpm: float | None = Form(None),
video: UploadFile | None = File(None), video: UploadFile | None = File(None),
cover: 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) has_video = bool(video and video.filename)
if bool(youtube_url) == has_video: if bool(youtube_url) == has_video:
raise HTTPException(400, "provide exactly one of youtube_url / video") raise HTTPException(400, "provide exactly one of youtube_url / video")
+6
View File
@@ -38,6 +38,12 @@ def test_youtube(tmp_path, monkeypatch):
assert meta["video_file"] == "source_video.webm" assert meta["video_file"] == "source_video.webm"
assert meta["thumbnail"] == "source_video.jpg" 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): def test_requires_exactly_one_source(tmp_path):
import pytest import pytest
from karaoke_maker.pipeline import PipelineError from karaoke_maker.pipeline import PipelineError
+25
View File
@@ -52,6 +52,16 @@ def test_failure_reported(tmp_path):
assert job["error"] == "boom" 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): def test_requires_one_source(tmp_path):
client, _ = make_client(tmp_path, ok_runner) client, _ = make_client(tmp_path, ok_runner)
resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x"}) 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() html = _index_html()
body = _fn_body(html, "paintCard") body = _fn_body(html, "paintCard")
assert "rebuilt_from" in body assert "rebuilt_from" in body
assert '" rebuilt"' in body
assert '"sr-only"' in _fn_body(html, "jobCard") 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 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): def test_rebuild_rejects_a_missing_upload_that_would_be_needed(tmp_path):
client, _ = make_client(tmp_path, ok_runner) client, _ = make_client(tmp_path, ok_runner)
resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x"}, resp = client.post("/api/jobs", data={"title": "T", "artist": "A", "lyrics": "x"},