Files
serato-doctor/tests/test_web.py
T
2026-07-02 16:17:11 -07:00

152 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import runpy
from pathlib import Path
import pytest
from serato_doctor.web import (
STATIC_FILES,
_file_token,
_verified_audio,
analyze_paths,
duplicate_repair,
duplicate_repair_batch,
)
def test_web_analysis_uses_production_health_pipeline(tmp_path):
generator = runpy.run_path(
str(Path(__file__).parents[1] / "samples/small-library/generate.py")
)
sample = generator["build_sample"](tmp_path / "sample")
result = analyze_paths(
sample / "Serato" / "_Serato_", sample / "Music"
)
assert result["score"] == 77.8
assert result["disk_tracks"] == 10
assert result["missing_references"] == 2
assert result["static_crates"] == 5
assert result["smart_crates"] == 2
assert result["database_entries"] == 10
assert result["database_library_matches"] == 10
assert result["database_missing_paths"] == 0
assert result["database_missing_unique_filenames"] == 0
assert result["tracks_missing_from_database"] == 0
assert result["details"]["old_crate_references"]["total"] == 2
assert result["details"]["old_crate_references"]["items"][0]["crate"]
assert result["details"]["suggested_matches"]["total"] == 0
assert result["details"]["unused_tracks"]["total"] == 1
assert result["orphan_candidates"] == 0
assert result["details"]["orphan_candidates"]["assessed"] is True
def test_web_analysis_rejects_missing_folders(tmp_path):
with pytest.raises(ValueError, match="Serato folder does not exist"):
analyze_paths(tmp_path / "missing", tmp_path)
def test_web_static_assets_are_declared_and_packaged():
asset_root = Path(__file__).parents[1] / "serato_doctor" / "webui"
assert set(STATIC_FILES) == {
"/",
"/app.css",
"/recovery.css",
"/layout-fixes.css",
"/app.js",
}
assert all((asset_root / filename).is_file() for filename, _ in STATIC_FILES.values())
html = (asset_root / "index.html").read_text(encoding="utf-8")
assert "Missing tracks in Serato" in html
assert "Old crate references" in html
assert "Possible orphan files" in html
assert "Choose a diagnostic" in html
assert "Backup recovery" in html
assert "Heres exactly what will happen" in html
assert 'id="batch-preview-modal"' in html
assert 'data-detail="database_missing_tracks"' in html
assert 'data-detail="old_crate_references"' in html
assert html.count('class="info-button"') >= 10
def test_duplicate_repair_preview_does_not_change_files(tmp_path):
serato = tmp_path / "_Serato_"
serato.mkdir()
music = tmp_path / "Music"
first = music / "A" / "Track.mp3"
second = music / "B" / "Track.mp3"
first.parent.mkdir(parents=True)
second.parent.mkdir(parents=True)
first.write_bytes(b"first")
second.write_bytes(b"second")
result = duplicate_repair(
serato, music, first, (first, second), backup_limit=10
)
assert result["applied"] is False
assert result["database_v2_modified"] is False
assert second.read_bytes() == b"second"
assert not second.is_symlink()
def test_audio_preview_tokens_only_open_signed_audio(tmp_path):
track = tmp_path / "Track.mp3"
track.write_bytes(b"audio")
token = _file_token(track)
assert _verified_audio(token) == track
with pytest.raises(ValueError, match="Invalid or expired"):
_verified_audio(token + "changed")
def test_duplicate_details_include_preview_and_finder_controls(tmp_path):
serato = tmp_path / "_Serato_"
serato.mkdir()
music = tmp_path / "Music"
first = music / "A" / "Track.mp3"
second = music / "B" / "Track.mp3"
first.parent.mkdir(parents=True)
second.parent.mkdir(parents=True)
first.write_bytes(b"first")
second.write_bytes(b"second")
result = analyze_paths(serato, music)
group = result["details"]["duplicate_filenames"]["items"][0]
assert len(group["file_previews"]) == 2
assert group["file_previews"][0]["audio_url"].startswith("/api/audio?")
assert group["file_previews"][0]["reveal_token"]
def test_batch_preview_combines_approved_groups_without_changes(tmp_path):
serato = tmp_path / "_Serato_"
serato.mkdir()
music = tmp_path / "Music"
choices = []
for filename in ("First.mp3", "Second.mp3"):
keeper = music / "A" / filename
duplicate = music / "B" / filename
keeper.parent.mkdir(parents=True, exist_ok=True)
duplicate.parent.mkdir(parents=True, exist_ok=True)
keeper.write_bytes(b"keeper")
duplicate.write_bytes(b"duplicate")
choices.append(
{"keeper": str(keeper), "group_files": [str(keeper), str(duplicate)]}
)
result = duplicate_repair_batch(
serato, music, choices, backup_limit=10
)
assert result["applied"] is False
assert result["choice_count"] == 2
assert len(result["replaced"]) == 2
assert len(result["decisions"]) == 2
assert result["decisions"][0]["keeper"].endswith("First.mp3")
assert result["decisions"][0]["hash_status"] == "different"
assert len(result["decisions"][0]["replaced"]) == 1
assert all(not Path(choice["group_files"][1]).is_symlink() for choice in choices)