import runpy from pathlib import Path import pytest from serato_doctor.web import ( STATIC_FILES, _file_token, _verified_audio, analyze_paths, duplicate_repair, ) 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 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 "Choose a diagnostic" in html assert "Backup recovery" 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"]