from pathlib import Path import pytest from serato_doctor.repair import ( BACKUP_FOLDER, apply_duplicate_repair, plan_duplicate_repair, restore_backup, rotate_backups, ) def library(tmp_path): serato = tmp_path / "_Serato_" crate = serato / "Subcrates" / "House.crate" crate.parent.mkdir(parents=True) crate.write_bytes(b"crate-data") (serato / "database V2").write_bytes(b"database-data") music = tmp_path / "Music" keeper = music / "Main" / "Track.mp3" duplicate = music / "Old" / "Track.mp3" keeper.parent.mkdir(parents=True) duplicate.parent.mkdir(parents=True) keeper.write_bytes(b"keeper") duplicate.write_bytes(b"duplicate") return serato, keeper, duplicate def test_duplicate_repair_backs_up_then_preserves_old_path_as_link(tmp_path): serato, keeper, duplicate = library(tmp_path) plan = plan_duplicate_repair(keeper, (keeper, duplicate), serato) receipt = apply_duplicate_repair(plan, serato, backup_limit=3) assert duplicate.is_symlink() assert duplicate.resolve() == keeper.resolve() assert (receipt.backup / "manifest.json").is_file() assert any((receipt.backup / "serato-metadata").rglob("House.crate")) assert any((receipt.backup / "serato-metadata").rglob("database V2")) restored = restore_backup(receipt.backup) assert restored == (duplicate.resolve(),) assert not duplicate.is_symlink() assert duplicate.read_bytes() == b"duplicate" def test_plan_rejects_keeper_outside_duplicate_group(tmp_path): serato, keeper, duplicate = library(tmp_path) outsider = tmp_path / "outsider.mp3" outsider.write_bytes(b"other") with pytest.raises(ValueError, match="must belong"): plan_duplicate_repair(outsider, (keeper, duplicate), serato) def test_backup_rotation_can_be_limited_or_unlimited(tmp_path): root = tmp_path / BACKUP_FOLDER for name in ("001", "002", "003"): backup = root / name backup.mkdir(parents=True) (backup / "manifest.json").write_text("{}", encoding="utf-8") rotate_backups(root, None) assert len(list(root.iterdir())) == 3 rotate_backups(root, 2) assert {path.name for path in root.iterdir()} == {"002", "003"}