"""Tests for organizer module.""" import pytest from tvh2jellyfin.organizer import ( generate_destination_path, generate_destination_path_with_episode, sanitize_directory_name, sanitize_filename, ) from tvh2jellyfin.tvdb import EpisodeInfo from tvh2jellyfin.tvheadend import Recording class TestSanitizeFilename: """Tests for filename sanitization.""" def test_removes_invalid_characters(self): assert sanitize_filename('Test: "Show"') == "Test Show" # Multiple spaces get normalized to single space assert sanitize_filename("Show <1> | Part 2") == "Show 1 Part 2" def test_normalizes_spaces(self): assert sanitize_filename("Test Show") == "Test Show" assert sanitize_filename("Test_Show") == "Test Show" def test_strips_whitespace(self): assert sanitize_filename(" Test Show ") == "Test Show" def test_empty_returns_unknown(self): assert sanitize_filename("") == "Unknown" assert sanitize_filename(" ") == "Unknown" class TestSanitizeDirectoryName: """Tests for directory name sanitization.""" def test_removes_trailing_dots(self): assert sanitize_directory_name("Test Show.") == "Test Show" assert sanitize_directory_name("Test Show...") == "Test Show" def test_handles_empty(self): assert sanitize_directory_name("") == "Unknown" class TestGenerateDestinationPath: """Tests for destination path generation.""" @pytest.fixture def recording_with_subtitle(self): return Recording( uuid="123", title="Test Show", subtitle="Episode Title", filename="/recordings/test.ts", filesize=1000000, channel="BBC One", start=1705312800, stop=1705316400, status="Completed OK", ) @pytest.fixture def recording_without_subtitle(self): return Recording( uuid="456", title="Another Show", subtitle="", filename="/recordings/another.ts", filesize=2000000, channel="ITV", start=1705312800, stop=1705316400, status="Completed OK", ) def test_with_subtitle(self, recording_with_subtitle): dest_dir, filename = generate_destination_path( recording_with_subtitle, "/media/tv" ) assert dest_dir == "/media/tv/Test Show" assert filename == "Test Show - Episode Title.ts" def test_without_subtitle(self, recording_without_subtitle): dest_dir, filename = generate_destination_path( recording_without_subtitle, "/media/tv" ) assert dest_dir == "/media/tv/Another Show" # Should include timestamp assert filename.startswith("Another Show - ") assert filename.endswith(".ts") class TestGenerateDestinationPathWithEpisode: """Tests for destination path generation with episode info.""" @pytest.fixture def recording(self): return Recording( uuid="123", title="Coronation Street", subtitle="", filename="/recordings/Coronation-Street/Coronation Street2026-01-06.ts", filesize=1000000, channel="ITV", start=1767744000, # 2026-01-06 stop=1767747600, status="Completed OK", ) @pytest.fixture def episode_info(self): return EpisodeInfo( series_id=456, series_name="Coronation Street", season=67, episode=4, episode_name="Episode 4", air_date="2026-01-06", ) def test_with_episode_info(self, recording, episode_info): """Test Jellyfin naming format with episode info.""" dest_dir, filename = generate_destination_path_with_episode( recording, "/media/tv", episode_info ) assert dest_dir == "/media/tv/Coronation Street/Season 67" assert filename == "Coronation Street - S67E04.ts" def test_without_episode_info(self, recording): """Test fallback when episode info not available.""" dest_dir, filename = generate_destination_path_with_episode( recording, "/media/tv", None ) assert dest_dir == "/media/tv/Coronation Street" # Should use fallback logic (timestamp) assert filename.startswith("Coronation Street - ") assert filename.endswith(".ts") def test_uses_canonical_series_name(self, recording): """Test that canonical series name from TVDB is used.""" episode_info = EpisodeInfo( series_id=456, series_name="Doctor Who (2005)", # TVDB canonical name season=14, episode=1, episode_name="The Church on Ruby Road", air_date="2023-12-25", ) # Recording might have different name recording_with_diff_name = Recording( uuid="789", title="Doctor Who", # TVHeadend name subtitle="", filename="/recordings/doctor-who.ts", filesize=3000000, channel="BBC One", start=1703512800, stop=1703516400, status="Completed OK", ) dest_dir, filename = generate_destination_path_with_episode( recording_with_diff_name, "/media/tv", episode_info ) # Should use TVDB canonical name with season folder assert dest_dir == "/media/tv/Doctor Who (2005)/Season 14" assert filename == "Doctor Who (2005) - S14E01.ts" def test_large_episode_numbers(self, recording): """Test with season/episode numbers > 99.""" episode_info = EpisodeInfo( series_id=456, series_name="Long Running Show", season=67, episode=125, episode_name="Episode", air_date="2026-01-06", ) dest_dir, filename = generate_destination_path_with_episode( recording, "/media/tv", episode_info ) assert filename == "Long Running Show - S67E125.ts" def test_preserves_file_extension(self, recording): """Test that file extension is preserved.""" recording_mkv = Recording( uuid="123", title="Test Show", subtitle="", filename="/recordings/test.mkv", filesize=1000000, channel="BBC", start=1705312800, stop=1705316400, status="Completed OK", ) episode_info = EpisodeInfo( series_id=456, series_name="Test Show", season=1, episode=1, episode_name="Pilot", air_date="2024-01-15", ) _, filename = generate_destination_path_with_episode( recording_mkv, "/media/tv", episode_info ) assert filename.endswith(".mkv")