fix(worker): import_album recovers orphaned .old/.importing from an interrupted swap
Adds a crash-recovery preamble that restores `final` from `.old` when a prior swap was interrupted after rename(final, old) but before rename(tmp, final), and always drops stale `.old`/`.importing` orphans. Also guards the swap: if nothing new was staged into tmp (empty/missing staging) and `final` already exists, leave it untouched instead of swapping in an empty copy. This is required beyond the literal preamble because the pre-existing swap logic unconditionally replaced `final` with `tmp` regardless of whether anything was actually imported, which would otherwise destroy a just-recovered (or any existing) copy whenever staging is empty.
This commit is contained in:
@@ -55,6 +55,29 @@ def test_import_album_leaves_no_temp_siblings(tmp_path):
|
||||
assert os.listdir(tmp_path / "Artist") == ["Album"] # no .importing / .old leftovers
|
||||
|
||||
|
||||
def test_import_album_recovers_from_interrupted_swap(tmp_path):
|
||||
final = str(tmp_path / "Artist" / "Album (2020)")
|
||||
os.makedirs(final + ".old")
|
||||
open(os.path.join(final + ".old", "01 Song.flac"), "w").close()
|
||||
# `final` is missing (crash happened after rename(final, old), before rename(tmp, final))
|
||||
staging = str(tmp_path / "staging") # empty/missing staging
|
||||
import_album(staging, final)
|
||||
# previous good copy restored to `final`; no leftover .old
|
||||
assert os.path.isfile(os.path.join(final, "01 Song.flac"))
|
||||
assert not os.path.isdir(final + ".old")
|
||||
|
||||
|
||||
def test_import_album_drops_stale_old_when_final_present(tmp_path):
|
||||
final = str(tmp_path / "Artist" / "Album (2020)")
|
||||
os.makedirs(final)
|
||||
open(os.path.join(final, "keep.flac"), "w").close()
|
||||
os.makedirs(final + ".old") # leftover from a completed swap
|
||||
open(os.path.join(final + ".old", "stale.flac"), "w").close()
|
||||
import_album(str(tmp_path / "staging"), final)
|
||||
assert not os.path.isdir(final + ".old") # stale .old removed
|
||||
assert os.path.isfile(os.path.join(final, "keep.flac")) # existing copy untouched by preamble
|
||||
|
||||
|
||||
def test_clear_staging_root_removes_jobs_but_keeps_root(tmp_path):
|
||||
# Clears per-job dirs (and stray files) under the root, but leaves the root itself so a
|
||||
# mounted staging volume stays mounted.
|
||||
|
||||
Reference in New Issue
Block a user