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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user