"""MusicBrainz picks an arbitrary releases[0] that is often a deluxe/expanded edition with more tracks (and a longer runtime) than the standard album a source legitimately delivers. Completeness and the duration guard must be judged against what the CHOSEN SOURCE offered, not MB's canonical edition — otherwise a complete standard-edition download is falsely rejected as 'incomplete' (The Script "No Sound Without Silence": 11 delivered vs MB 12) or 'too short' (Skillet "Unleashed": 12 delivered ~40 min vs MB's 20-track deluxe ~73 min). A genuinely short download must still fail. """ import os from dataclasses import replace from lyra_worker.adapters.fakes import FakeQobuz from lyra_worker.claim import claim_next from lyra_worker.pipeline import run_pipeline from lyra_worker.types import DownloadResult, MBTarget from tests.conftest import insert_request class _StandardEditionQobuz(FakeQobuz): """Qobuz-like source offering a fixed-size *standard* edition (source_tracks), independent of MB's possibly-larger deluxe count; stages that many real (non-empty) files and reports them.""" def __init__(self, source_tracks): super().__init__() self._n = source_tracks def search(self, target): return super().search(replace(target, track_count=self._n)) def download(self, candidate, dest, on_progress): os.makedirs(dest, exist_ok=True) for i in range(candidate.track_count): with open(os.path.join(dest, f"{i + 1:02d} Track.flac"), "wb") as fh: fh.write(b"audio") on_progress(1.0) return DownloadResult(ok=True, path=dest, track_count=candidate.track_count) class _PartialQobuz(FakeQobuz): """Promises `promised` tracks but one fails mid-download: stages and reports `promised - 1` (the real John Mayer "Roll it on Home" IncompleteRead case).""" def __init__(self, promised): super().__init__() self._n = promised def search(self, target): return super().search(replace(target, track_count=self._n)) def download(self, candidate, dest, on_progress): os.makedirs(dest, exist_ok=True) delivered = candidate.track_count - 1 for i in range(delivered): with open(os.path.join(dest, f"{i + 1:02d} Track.flac"), "wb") as fh: fh.write(b"audio") return DownloadResult(ok=True, path=dest, track_count=delivered) class _DeluxeResolver: """Resolver whose canonical release is a larger (deluxe) edition than the source delivers.""" def __init__(self, mb_tracks, mb_total_s): self._t, self._s = mb_tracks, mb_total_s def resolve(self, artist, album): return MBTarget(artist=artist, album=album, track_count=self._t, total_duration_s=self._s) def _state_error(conn, job_id): with conn.cursor() as cur: cur.execute('SELECT state, error FROM "Job" WHERE id = %s', (job_id,)) return cur.fetchone() def test_standard_edition_not_rejected_as_incomplete(conn): # The Script: source delivers the 11-track standard edition; MB's canonical release is 12. job_id = insert_request(conn, artist="The Script", album="No Sound Without Silence") claim_next(conn) run_pipeline(conn, job_id, [_StandardEditionQobuz(source_tracks=11)], resolver=_DeluxeResolver(mb_tracks=12, mb_total_s=2943), measure_duration=lambda _s: 2700.0, dest_root="/tmp/lib-se") assert _state_error(conn, job_id) == ("imported", None) def test_smaller_edition_not_rejected_as_too_short(conn): # Skillet: source delivers the 12-track standard (~40 min); MB's release is a 20-track deluxe # (~73 min). The unscaled duration guard falsely rejects; scaling to the delivered edition # (12/20) must let it pass. job_id = insert_request(conn, artist="Skillet", album="Unleashed") claim_next(conn) run_pipeline(conn, job_id, [_StandardEditionQobuz(source_tracks=12)], resolver=_DeluxeResolver(mb_tracks=20, mb_total_s=4380), measure_duration=lambda _s: 2400.0, dest_root="/tmp/lib-se") assert _state_error(conn, job_id) == ("imported", None) def test_genuinely_incomplete_still_rejected(conn): # John Mayer: the source promised 12 tracks but one failed mid-download → only 11 land. # This is a real partial and must still be rejected (the fix must not swallow it). job_id = insert_request(conn, artist="John Mayer", album="The Search for Everything") claim_next(conn) run_pipeline(conn, job_id, [_PartialQobuz(promised=12)], resolver=_DeluxeResolver(mb_tracks=12, mb_total_s=2580), measure_duration=lambda _s: 2400.0, dest_root="/tmp/lib-se") assert _state_error(conn, job_id) == ("needs_attention", "incomplete download")