"""Edge case tests for domain entities and value objects.""" import pytest from datetime import datetime from alfred.domain.movies.entities import Movie from alfred.domain.movies.value_objects import MovieTitle, Quality, ReleaseYear from alfred.domain.shared.exceptions import ValidationError from alfred.domain.shared.value_objects import FilePath, FileSize, ImdbId from alfred.domain.subtitles.entities import Subtitle from alfred.domain.subtitles.value_objects import Language, SubtitleFormat, TimingOffset from alfred.domain.tv_shows.entities import TVShow from alfred.domain.tv_shows.value_objects import ShowStatus class TestImdbIdEdgeCases: """Edge case tests for ImdbId.""" def test_valid_imdb_id(self): """Should accept valid IMDb ID.""" imdb_id = ImdbId("tt1375666") assert str(imdb_id) == "tt1375666" def test_imdb_id_with_leading_zeros(self): """Should accept IMDb ID with leading zeros.""" imdb_id = ImdbId("tt0000001") assert str(imdb_id) == "tt0000001" def test_imdb_id_long_number(self): """Should accept IMDb ID with 8 digits.""" imdb_id = ImdbId("tt12345678") assert str(imdb_id) == "tt12345678" def test_imdb_id_lowercase(self): """Should accept lowercase tt prefix.""" imdb_id = ImdbId("tt1234567") assert str(imdb_id) == "tt1234567" def test_imdb_id_uppercase(self): """Should handle uppercase TT prefix.""" # Behavior depends on implementation try: imdb_id = ImdbId("TT1234567") # If accepted, should work assert imdb_id is not None except (ValidationError, ValueError): # If rejected, that's also valid pass def test_imdb_id_without_prefix(self): """Should reject ID without tt prefix.""" with pytest.raises((ValidationError, ValueError)): ImdbId("1234567") def test_imdb_id_empty(self): """Should reject empty string.""" with pytest.raises((ValidationError, ValueError)): ImdbId("") def test_imdb_id_none(self): """Should reject None.""" with pytest.raises((ValidationError, ValueError, TypeError)): ImdbId(None) def test_imdb_id_with_spaces(self): """Should reject ID with spaces.""" with pytest.raises((ValidationError, ValueError)): ImdbId("tt 1234567") def test_imdb_id_with_special_chars(self): """Should reject ID with special characters.""" with pytest.raises((ValidationError, ValueError)): ImdbId("tt1234567!") def test_imdb_id_equality(self): """Should compare equal IDs.""" id1 = ImdbId("tt1234567") id2 = ImdbId("tt1234567") assert id1 == id2 or str(id1) == str(id2) def test_imdb_id_hash(self): """Should be hashable for use in sets/dicts.""" id1 = ImdbId("tt1234567") id2 = ImdbId("tt1234567") # Should be usable in set _s = {id1, id2} # Test hashability # Depending on implementation, might be 1 or 2 items class TestFilePathEdgeCases: """Edge case tests for FilePath.""" def test_absolute_path(self): """Should accept absolute path.""" path = FilePath("/home/user/movies/movie.mkv") assert "/home/user/movies/movie.mkv" in str(path) def test_relative_path(self): """Should accept relative path.""" path = FilePath("movies/movie.mkv") assert "movies/movie.mkv" in str(path) def test_path_with_spaces(self): """Should accept path with spaces.""" path = FilePath("/home/user/My Movies/movie file.mkv") assert "My Movies" in str(path) def test_path_with_unicode(self): """Should accept path with unicode.""" path = FilePath("/home/user/映画/日本語.mkv") assert "映画" in str(path) def test_windows_path(self): """Should handle Windows-style path.""" path = FilePath("C:\\Users\\user\\Movies\\movie.mkv") assert "movie.mkv" in str(path) def test_empty_path(self): """Should handle empty path.""" try: path = FilePath("") # If accepted, may return "." for current directory assert str(path) in ["", "."] except (ValidationError, ValueError): # If rejected, that's also valid pass def test_path_with_dots(self): """Should handle path with . and ..""" path = FilePath("/home/user/../other/./movie.mkv") assert "movie.mkv" in str(path) class TestFileSizeEdgeCases: """Edge case tests for FileSize.""" def test_zero_size(self): """Should accept zero size.""" size = FileSize(0) assert size.bytes == 0 def test_very_large_size(self): """Should accept very large size (petabytes).""" size = FileSize(1024**5) # 1 PB assert size.bytes == 1024**5 def test_negative_size(self): """Should reject negative size.""" with pytest.raises((ValidationError, ValueError)): FileSize(-1) def test_human_readable_bytes(self): """Should format bytes correctly.""" size = FileSize(500) readable = size.to_human_readable() assert "500" in readable or "B" in readable def test_human_readable_kb(self): """Should format KB correctly.""" size = FileSize(1024) readable = size.to_human_readable() assert "KB" in readable or "1" in readable def test_human_readable_mb(self): """Should format MB correctly.""" size = FileSize(1024 * 1024) readable = size.to_human_readable() assert "MB" in readable or "1" in readable def test_human_readable_gb(self): """Should format GB correctly.""" size = FileSize(1024 * 1024 * 1024) readable = size.to_human_readable() assert "GB" in readable or "1" in readable class TestMovieTitleEdgeCases: """Edge case tests for MovieTitle.""" def test_normal_title(self): """Should accept normal title.""" title = MovieTitle("Inception") assert title.value == "Inception" def test_title_with_year(self): """Should accept title with year.""" title = MovieTitle("Blade Runner 2049") assert "2049" in title.value def test_title_with_special_chars(self): """Should accept title with special characters.""" title = MovieTitle("Se7en") assert title.value == "Se7en" def test_title_with_colon(self): """Should accept title with colon.""" title = MovieTitle("Star Wars: A New Hope") assert ":" in title.value def test_title_with_unicode(self): """Should accept unicode title.""" title = MovieTitle("千と千尋の神隠し") assert title.value == "千と千尋の神隠し" def test_empty_title(self): """Should reject empty title.""" with pytest.raises((ValidationError, ValueError)): MovieTitle("") def test_whitespace_title(self): """Should handle whitespace title (may strip or reject).""" try: title = MovieTitle(" ") # If accepted after stripping, that's valid assert title.value is not None except (ValidationError, ValueError): # If rejected, that's also valid pass def test_very_long_title(self): """Should handle very long title.""" long_title = "A" * 1000 try: title = MovieTitle(long_title) assert len(title.value) == 1000 except (ValidationError, ValueError): # If there's a length limit, that's valid pass class TestReleaseYearEdgeCases: """Edge case tests for ReleaseYear.""" def test_valid_year(self): """Should accept valid year.""" year = ReleaseYear(2024) assert year.value == 2024 def test_old_movie_year(self): """Should accept old movie year.""" year = ReleaseYear(1895) # First movie ever assert year.value == 1895 def test_future_year(self): """Should accept near future year.""" year = ReleaseYear(2030) assert year.value == 2030 def test_very_old_year(self): """Should reject very old year.""" with pytest.raises((ValidationError, ValueError)): ReleaseYear(1800) def test_very_future_year(self): """Should reject very future year.""" with pytest.raises((ValidationError, ValueError)): ReleaseYear(3000) def test_negative_year(self): """Should reject negative year.""" with pytest.raises((ValidationError, ValueError)): ReleaseYear(-2024) def test_zero_year(self): """Should reject zero year.""" with pytest.raises((ValidationError, ValueError)): ReleaseYear(0) class TestQualityEdgeCases: """Edge case tests for Quality.""" def test_standard_qualities(self): """Should accept standard qualities.""" qualities = [ (Quality.SD, "480p"), (Quality.HD, "720p"), (Quality.FULL_HD, "1080p"), (Quality.UHD_4K, "2160p"), ] for quality_enum, expected_value in qualities: assert quality_enum.value == expected_value def test_unknown_quality(self): """Should accept unknown quality.""" quality = Quality.UNKNOWN assert quality.value == "unknown" def test_from_string_quality(self): """Should parse quality from string.""" assert Quality.from_string("1080p") == Quality.FULL_HD assert Quality.from_string("720p") == Quality.HD assert Quality.from_string("2160p") == Quality.UHD_4K assert Quality.from_string("HDTV") == Quality.UNKNOWN def test_empty_quality(self): """Should handle empty quality string.""" quality = Quality.from_string("") assert quality == Quality.UNKNOWN class TestShowStatusEdgeCases: """Edge case tests for ShowStatus.""" def test_all_statuses(self): """Should have all expected statuses.""" assert ShowStatus.ONGOING is not None assert ShowStatus.ENDED is not None assert ShowStatus.UNKNOWN is not None def test_from_string_valid(self): """Should parse valid status strings.""" assert ShowStatus.from_string("ongoing") == ShowStatus.ONGOING assert ShowStatus.from_string("ended") == ShowStatus.ENDED def test_from_string_case_insensitive(self): """Should be case insensitive.""" assert ShowStatus.from_string("ONGOING") == ShowStatus.ONGOING assert ShowStatus.from_string("Ended") == ShowStatus.ENDED def test_from_string_unknown(self): """Should return UNKNOWN for invalid strings.""" assert ShowStatus.from_string("invalid") == ShowStatus.UNKNOWN assert ShowStatus.from_string("") == ShowStatus.UNKNOWN class TestLanguageEdgeCases: """Edge case tests for Language.""" def test_common_languages(self): """Should have common languages.""" assert Language.ENGLISH is not None assert Language.FRENCH is not None def test_from_code_valid(self): """Should parse valid language codes.""" assert Language.from_code("en") == Language.ENGLISH assert Language.from_code("fr") == Language.FRENCH def test_from_code_case_insensitive(self): """Should be case insensitive.""" assert Language.from_code("EN") == Language.ENGLISH assert Language.from_code("Fr") == Language.FRENCH def test_from_code_unknown(self): """Should handle unknown codes.""" # Behavior depends on implementation try: lang = Language.from_code("xx") # If it returns something, that's valid assert lang is not None except (ValidationError, ValueError, KeyError): # If it raises, that's also valid pass class TestSubtitleFormatEdgeCases: """Edge case tests for SubtitleFormat.""" def test_common_formats(self): """Should have common formats.""" assert SubtitleFormat.SRT is not None assert SubtitleFormat.ASS is not None def test_from_extension_with_dot(self): """Should handle extension with dot.""" fmt = SubtitleFormat.from_extension(".srt") assert fmt == SubtitleFormat.SRT def test_from_extension_without_dot(self): """Should handle extension without dot.""" fmt = SubtitleFormat.from_extension("srt") assert fmt == SubtitleFormat.SRT def test_from_extension_case_insensitive(self): """Should be case insensitive.""" assert SubtitleFormat.from_extension("SRT") == SubtitleFormat.SRT assert SubtitleFormat.from_extension(".ASS") == SubtitleFormat.ASS class TestTimingOffsetEdgeCases: """Edge case tests for TimingOffset.""" def test_zero_offset(self): """Should accept zero offset.""" offset = TimingOffset(0) assert offset.milliseconds == 0 def test_positive_offset(self): """Should accept positive offset.""" offset = TimingOffset(5000) assert offset.milliseconds == 5000 def test_negative_offset(self): """Should accept negative offset.""" offset = TimingOffset(-5000) assert offset.milliseconds == -5000 def test_very_large_offset(self): """Should accept very large offset.""" offset = TimingOffset(3600000) # 1 hour assert offset.milliseconds == 3600000 class TestMovieEntityEdgeCases: """Edge case tests for Movie entity.""" def test_minimal_movie(self): """Should create movie with minimal fields.""" movie = Movie( imdb_id=ImdbId("tt1234567"), title=MovieTitle("Test"), quality=Quality.UNKNOWN, ) assert movie.imdb_id is not None def test_full_movie(self): """Should create movie with all fields.""" movie = Movie( imdb_id=ImdbId("tt1234567"), title=MovieTitle("Test Movie"), release_year=ReleaseYear(2024), quality=Quality.FULL_HD, file_path=FilePath("/movies/test.mkv"), file_size=FileSize(1000000000), tmdb_id=12345, added_at=datetime.now(), ) assert movie.tmdb_id == 12345 def test_movie_without_optional_fields(self): """Should handle None optional fields.""" movie = Movie( imdb_id=ImdbId("tt1234567"), title=MovieTitle("Test"), release_year=None, quality=Quality.UNKNOWN, file_path=None, file_size=None, tmdb_id=None, ) assert movie.release_year is None assert movie.file_path is None class TestTVShowEntityEdgeCases: """Edge case tests for TVShow entity.""" def test_minimal_show(self): """Should create show with minimal fields.""" show = TVShow( imdb_id=ImdbId("tt1234567"), title="Test Show", seasons_count=1, status=ShowStatus.UNKNOWN, ) assert show.title == "Test Show" def test_show_with_zero_seasons(self): """Should handle show with zero seasons.""" show = TVShow( imdb_id=ImdbId("tt1234567"), title="Upcoming Show", seasons_count=0, status=ShowStatus.ONGOING, ) assert show.seasons_count == 0 def test_show_with_many_seasons(self): """Should handle show with many seasons.""" show = TVShow( imdb_id=ImdbId("tt1234567"), title="Long Running Show", seasons_count=50, status=ShowStatus.ONGOING, ) assert show.seasons_count == 50 class TestSubtitleEntityEdgeCases: """Edge case tests for Subtitle entity.""" def test_minimal_subtitle(self): """Should create subtitle with minimal fields.""" subtitle = Subtitle( media_imdb_id=ImdbId("tt1234567"), language=Language.ENGLISH, format=SubtitleFormat.SRT, file_path=FilePath("/subs/test.srt"), ) assert subtitle.language == Language.ENGLISH def test_subtitle_for_episode(self): """Should create subtitle for specific episode.""" subtitle = Subtitle( media_imdb_id=ImdbId("tt1234567"), language=Language.ENGLISH, format=SubtitleFormat.SRT, file_path=FilePath("/subs/s01e01.srt"), season_number=1, episode_number=1, ) assert subtitle.season_number == 1 assert subtitle.episode_number == 1 def test_subtitle_with_all_metadata(self): """Should create subtitle with all metadata.""" subtitle = Subtitle( media_imdb_id=ImdbId("tt1234567"), language=Language.ENGLISH, format=SubtitleFormat.SRT, file_path=FilePath("/subs/test.srt"), timing_offset=TimingOffset(500), hearing_impaired=True, forced=True, source="OpenSubtitles", uploader="user123", download_count=10000, rating=9.5, ) assert subtitle.hearing_impaired is True assert subtitle.forced is True assert subtitle.rating == 9.5