"""Unit tests for duplicate resolution strategies.""" import pytest from pathlib import Path from vlm.models import MovieIdentity from vlm.duplicate_resolve import choose_keep_index def _mi(title: str = "Test", year: int | None = 2020) -> MovieIdentity: return MovieIdentity( title=title, year=year, confidence=0.9, needs_review=False, original_filename="test.mkv", ) class TestByQuality: """Tests for by_quality strategy.""" def test_by_quality_prefers_higher_resolution(self): """1080p vs 720p -> keep 1080p.""" p1 = Path("/movies/Test.2020.720p.BluRay.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1280x720", "size_bytes": 1000000}, {"path": str(p2), "resolution": "1920x1080", "size_bytes": 2000000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 # 1080p def test_by_quality_prefers_bluray_over_webdl(self): """Same resolution: BluRay > WEB-DL.""" p1 = Path("/movies/Test.2020.1080p.WEB-DL.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1920x1080", "size_bytes": 2000000}, {"path": str(p2), "resolution": "1920x1080", "size_bytes": 2000000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 # BluRay def test_by_quality_prefers_hevc_over_h264(self): """Same resolution/source: x265 > x264.""" p1 = Path("/movies/Test.2020.1080p.BluRay.x264.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.x265.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1920x1080", "codec": "h264", "size_bytes": 1500000}, {"path": str(p2), "resolution": "1920x1080", "codec": "hevc", "size_bytes": 1200000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 # x265 def test_by_quality_uses_size_as_tiebreaker(self): """Same resolution/source/codec: larger file wins.""" p1 = Path("/movies/Test.2020.1080p.BluRay.x264.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.x264.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1920x1080", "size_bytes": 1000000}, {"path": str(p2), "resolution": "1920x1080", "size_bytes": 2000000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 # larger def test_by_quality_fallback_to_filename(self): """No resolution/codec in qc -> parse from filename.""" p1 = Path("/movies/Test.2020.720p.WEB-DL.mkv") p2 = Path("/movies/Test.2020.2160p.BluRay.x265.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "size_bytes": 0}, {"path": str(p2), "size_bytes": 0}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 # 2160p from filename def test_by_quality_returns_first_when_all_equal(self): """All equal -> keep first (index 0).""" p1 = Path("/movies/Test.2020.1080p.BluRay.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1920x1080", "size_bytes": 1000000}, {"path": str(p2), "resolution": "1920x1080", "size_bytes": 1000000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 0 def test_by_quality_deprioritizes_sample(self): """Sample clip should lose to normal release when quality is equal.""" p1 = Path("/movies/Test.2020.Sample.1080p.BluRay.mkv") p2 = Path("/movies/Test.2020.1080p.BluRay.mkv") items = [(p1, _mi()), (p2, _mi())] qc = [ {"path": str(p1), "resolution": "1920x1080", "size_bytes": 1000000}, {"path": str(p2), "resolution": "1920x1080", "size_bytes": 1000000}, ] idx = choose_keep_index(items, "by_quality", quality_comparison=qc) assert idx == 1 class TestOtherStrategies: """Tests for by_reputation, first_seen, manual.""" def test_manual_returns_none(self): items = [(Path("/a.mkv"), _mi()), (Path("/b.mkv"), _mi())] assert choose_keep_index(items, "manual") is None def test_first_seen_returns_zero(self): items = [(Path("/a.mkv"), _mi()), (Path("/b.mkv"), _mi())] assert choose_keep_index(items, "first_seen") == 0 def test_by_reputation_prefers_higher_score(self): mi_low = _mi() mi_low.reputation_score = 6.0 mi_low.reputation_votes = 100 mi_high = _mi() mi_high.reputation_score = 8.5 mi_high.reputation_votes = 500 items = [ (Path("/a.mkv"), mi_low), (Path("/b.mkv"), mi_high), ] idx = choose_keep_index(items, "by_reputation") assert idx == 1 def test_by_reputation_deprioritizes_sample(self): """Sample clip should not be selected even with identical reputation.""" a = _mi() a.reputation_score = 8.0 a.reputation_votes = 300 b = _mi() b.reputation_score = 8.0 b.reputation_votes = 300 items = [ (Path("/a/Test.Sample.mkv"), a), (Path("/a/Test.Final.mkv"), b), ] idx = choose_keep_index(items, "by_reputation") assert idx == 1 def test_by_reputation_falls_back_to_quality_when_reputation_missing(self): """When reputation is missing, BluRay should beat WEB-DL.""" a = _mi() a.reputation_score = None a.reputation_votes = None b = _mi() b.reputation_score = None b.reputation_votes = None p_web = Path("/a/Test.2020.2160p.WEB-DL.HEVC.mkv") p_bluray = Path("/a/Test.2020.1080p.BluRay.x265.mkv") items = [(p_web, a), (p_bluray, b)] qc = [ {"path": str(p_web), "resolution": "3840x2160", "codec": "hevc", "size_bytes": 24000000000}, {"path": str(p_bluray), "resolution": "1920x1080", "codec": "hevc", "size_bytes": 8000000000}, ] idx = choose_keep_index(items, "by_reputation", quality_comparison=qc) assert idx == 1 def test_by_reputation_quality_time_prefers_quality_after_reputation(self): """When reputation ties, quality should decide before modified time.""" a = _mi() a.reputation_score = 8.0 a.reputation_votes = 300 b = _mi() b.reputation_score = 8.0 b.reputation_votes = 300 p1 = Path("/a/Test.2020.720p.WEB-DL.mkv") p2 = Path("/a/Test.2020.1080p.BluRay.mkv") items = [(p1, a), (p2, b)] qc = [ { "path": str(p1), "resolution": "1280x720", "size_bytes": 1000000, "modified_timestamp": "2026-02-01T00:00:00+00:00", }, { "path": str(p2), "resolution": "1920x1080", "size_bytes": 2000000, "modified_timestamp": "2025-01-01T00:00:00+00:00", }, ] idx = choose_keep_index(items, "by_reputation_quality_time", quality_comparison=qc) assert idx == 1 def test_by_reputation_quality_time_prefers_newer_when_rep_and_quality_tie(self): """When reputation and quality tie, newer modified timestamp wins.""" a = _mi() a.reputation_score = 8.0 a.reputation_votes = 300 b = _mi() b.reputation_score = 8.0 b.reputation_votes = 300 p1 = Path("/a/Test.2020.1080p.BluRay.mkv") p2 = Path("/a/Test.2020.1080p.BluRay.mkv") items = [(p1, a), (p2, b)] qc = [ { "path": str(p1), "resolution": "1920x1080", "size_bytes": 2000000, "modified_timestamp": "2025-01-01T00:00:00+00:00", }, { "path": str(p2), "resolution": "1920x1080", "size_bytes": 2000000, "modified_timestamp": "2026-01-01T00:00:00+00:00", }, ] idx = choose_keep_index(items, "by_reputation_quality_time", quality_comparison=qc) assert idx == 1