fix: atomic import swap, disk/DB replace consistency, guarded pipeline loop

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-11 15:27:48 +02:00
parent fe973343ad
commit 88ef4149d9
5 changed files with 119 additions and 26 deletions
+12
View File
@@ -41,6 +41,18 @@ def test_import_album_tolerates_missing_staging(tmp_path):
assert os.path.isdir(final) and os.listdir(final) == []
def test_import_album_leaves_no_temp_siblings(tmp_path):
final = tmp_path / "Artist" / "Album"
final.mkdir(parents=True)
(final / "old.flac").write_bytes(b"old")
staging = tmp_path / "stg"
staging.mkdir()
(staging / "01 New.flac").write_bytes(b"new")
import_album(str(staging), str(final))
assert sorted(os.listdir(final)) == ["01 New.flac"] # replaced
assert os.listdir(tmp_path / "Artist") == ["Album"] # no .importing / .old leftovers
def test_clear_staging_root_removes_staging(tmp_path):
stg = tmp_path / ".staging" / "j1"
stg.mkdir(parents=True)
+49
View File
@@ -68,3 +68,52 @@ def test_retry_imports_clean_with_no_orphans(conn, tmp_path):
final = tmp_path / "Radiohead" / "In Rainbows"
# exactly the 3 real tracks — the attempt-1 "orphan-a.flac" is NOT present
assert sorted(os.listdir(final)) == ["0 track.flac", "1 track.flac", "2 track.flac"]
def test_existing_equal_quality_copy_is_kept_not_replaced(conn, tmp_path):
# An existing lossy (q1) copy on disk + DB. A monitored upgrade job whose only candidate is also
# q1 (FakeYouTube) must NOT replace the files or the DB row.
import os as _os
from lyra_worker.adapters.fakes import FakeYouTube
from lyra_worker.library import album_dir
from lyra_worker.types import MBTarget
target = MBTarget(artist="A", album="B")
final = album_dir(str(tmp_path), target)
_os.makedirs(final, exist_ok=True)
open(_os.path.join(final, "existing.opus"), "wb").close()
with conn.cursor() as cur:
cur.execute(
"INSERT INTO \"MonitoredRelease\" (id, \"artistMbid\", \"artistName\", \"rgMbid\", album, "
"\"secondaryTypes\", monitored, state, \"currentQualityClass\", \"createdAt\") "
"VALUES (gen_random_uuid()::text, 'a1', 'A', 'rg1', 'B', '{}', true, 'grabbed', 1, now()) RETURNING id"
)
rel_id = cur.fetchone()[0]
cur.execute(
"INSERT INTO \"Request\" (id, artist, album, status, \"monitoredReleaseId\", \"createdAt\") "
"VALUES (gen_random_uuid()::text, 'A', 'B', 'pending', %s, now()) RETURNING id",
(rel_id,),
)
req = cur.fetchone()[0]
cur.execute(
"INSERT INTO \"LibraryItem\" (id, \"requestId\", artist, album, path, source, format, "
"\"qualityClass\", \"importedAt\") "
"VALUES (gen_random_uuid()::text, %s, 'A', 'B', %s, 'youtube', 'OPUS', 1, now())",
(req, final),
)
cur.execute(
"INSERT INTO \"Job\" (id, \"requestId\", state, \"currentStage\", attempts, \"createdAt\", \"updatedAt\") "
"VALUES (gen_random_uuid()::text, %s, 'requested', 'intake', 0, now(), now()) RETURNING id",
(req,),
)
job_id = cur.fetchone()[0]
conn.commit()
run_pipeline(conn, job_id, [FakeYouTube()], dest_root=str(tmp_path), upgrade_cutoff=2)
with conn.cursor() as cur:
cur.execute('SELECT state FROM "Job" WHERE id = %s', (job_id,))
assert cur.fetchone()[0] == "imported"
cur.execute('SELECT count(*) FROM "LibraryItem" WHERE artist=%s AND album=%s AND source=%s', ("A", "B", "youtube"))
assert cur.fetchone()[0] == 1 # the original row is untouched
assert _os.path.isfile(_os.path.join(final, "existing.opus")) # the existing files were kept