soulsync/tests/test_reorganize_deleted_quarantine.py
BoulderBadgeDad 20cd12e66b Reorganize: skip files in the duplicate-cleaner /deleted quarantine (#746)
The Duplicate Cleaner moves de-duplicated files into <transfer>/deleted/.
If a user's media server scans the transfer folder (e.g. a /music root
holding both the library and the transfer dir), those quarantined files
get real track rows in SoulSync's DB. Reorganize is purely DB-driven —
it acts on each track's stored file_path — so it would dutifully move a
quarantined file back OUT of /deleted to the template location, exactly
what Tacobell444 reported.

We can't stop the rows from existing (they come from the media server,
which the app doesn't control), so the fix is bounded to Reorganize, as
the reporter asked: skip any track whose resolved path is under
<transfer>/deleted. Surfaced as a non-matched 'In deleted/quarantine
folder — skipped' in the preview; apply mirrors it (post-process never
runs, file left in place, counted as skipped).

Detection is anchored to the <transfer>/deleted PREFIX (not a bare
substring) so a real album like 'Deleted Scenes' is kept; falls back to
an exact 'deleted' path-segment match when transfer_dir is unavailable
(mirrors the cleaner's own 'if deleted in dirs' skip). The one
unavoidable ambiguity — an artist folder named exactly 'deleted' at the
transfer root — is pinned in a test as intentional.

Guard added once where both consumers see it: preview_album_reorganize
and the apply worker (_RunContext gains transfer_dir).

Tests: tests/test_reorganize_deleted_quarantine.py (8 unit) +
test_library_reorganize_orchestrator.py (preview + apply integration,
differential-verified they fail without the fix). 128 adjacent
reorganize tests still green.
2026-05-30 00:15:06 -07:00

72 lines
3.4 KiB
Python

"""Reorganize must skip files in the duplicate-cleaner quarantine (#746).
The Duplicate Cleaner moves de-duplicated files into ``<transfer>/deleted/``.
If the user's media server scans the transfer folder (e.g. a ``/music`` root
holding both the library and the transfer dir), those quarantined files get real
rows in SoulSync's DB. Reorganize is purely DB-driven, so without a guard it
would move them back OUT of /deleted to the template location.
These tests pin:
1. ``_is_in_deleted_quarantine`` — the anchored detection, including the
false-positive guard (an album literally named "Deleted" elsewhere is kept).
2. ``preview_album_reorganize`` — a quarantined track is surfaced as a skip,
a normal track is not (proves the planner-shared guard fires on the path
both preview AND apply run through).
"""
from __future__ import annotations
import os
from core.library_reorganize import _is_in_deleted_quarantine
TRANSFER = os.path.join(os.sep, "music", "soulsync")
class TestIsInDeletedQuarantine:
def test_file_directly_in_quarantine_is_flagged(self):
p = os.path.join(TRANSFER, "deleted", "Artist", "Album", "01.flac")
assert _is_in_deleted_quarantine(p, TRANSFER) is True
def test_file_in_normal_album_is_not_flagged(self):
p = os.path.join(TRANSFER, "Artist", "Album", "01.flac")
assert _is_in_deleted_quarantine(p, TRANSFER) is False
def test_album_with_deleted_in_name_is_kept(self):
"""Anchored to the <transfer>/deleted PREFIX, not a substring — a
real album like 'Deleted Scenes' nested under an artist must NOT be
skipped."""
p = os.path.join(TRANSFER, "Artist", "Deleted Scenes", "01.flac")
assert _is_in_deleted_quarantine(p, TRANSFER) is False
def test_known_unavoidable_collision_is_documented(self):
"""The ONE genuine ambiguity: an artist folder named exactly
'deleted' sitting directly at the transfer root occupies the same
path as the duplicate-cleaner quarantine, so it IS treated as
quarantine. This is unavoidable (we can't tell a real 'deleted'
artist from the cleaner's dir) and accepted — pinned here so the
behavior is intentional, not a surprise. Differently-cased or
nested 'Deleted' names are safe (see the other tests)."""
collision = os.path.join(TRANSFER, "deleted", "Album", "01.flac")
assert _is_in_deleted_quarantine(collision, TRANSFER) is True
def test_substring_not_matched(self):
"""'Undeleted' / 'deleted_scenes' as a segment must not trip the
exact-segment / prefix check."""
p = os.path.join(TRANSFER, "Undeleted", "Album", "01.flac")
assert _is_in_deleted_quarantine(p, TRANSFER) is False
def test_no_transfer_dir_falls_back_to_segment_match(self):
p = os.path.join(os.sep, "anywhere", "deleted", "x.flac")
assert _is_in_deleted_quarantine(p, None) is True
p2 = os.path.join(os.sep, "anywhere", "Undeleted", "x.flac")
assert _is_in_deleted_quarantine(p2, None) is False
def test_empty_path_is_safe(self):
assert _is_in_deleted_quarantine(None, TRANSFER) is False
assert _is_in_deleted_quarantine("", TRANSFER) is False
def test_nested_subfolders_under_quarantine_flagged(self):
p = os.path.join(TRANSFER, "deleted", "a", "b", "c", "track.flac")
assert _is_in_deleted_quarantine(p, TRANSFER) is True