"""Tests for the releases domain entities.""" import dataclasses from datetime import UTC, datetime import pytest from alfred.domain.releases_TO_CHECK.entities import ( EpisodeRelease, MovieRelease, SeasonRelease, SeriesRelease, TrackProfile, ) from alfred.domain.releases_TO_CHECK.value_objects import EpisodeRange, ReleaseMode from alfred.domain.shared_TO_CHECK.exceptions import ValidationError from alfred.domain.shared_TO_CHECK.media import AudioTrack, SubtitleTrack from alfred.domain.shared_TO_CHECK.value_objects import FilePath, ImdbId, TmdbId from alfred.domain.tv_shows.value_objects import EpisodeNumber, SeasonNumber _ADDED_AT = datetime(2026, 5, 25, 8, 30, 0, tzinfo=UTC) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _ep(start: int, end: int | None = None, *, file: str = "x.mkv") -> EpisodeRelease: end_n = EpisodeNumber(end if end is not None else start) return EpisodeRelease( episodes=EpisodeRange(EpisodeNumber(start), end_n), file_path=FilePath(file), tracks=TrackProfile(), ) # --------------------------------------------------------------------------- # TrackProfile # --------------------------------------------------------------------------- class TestTrackProfile: def test_default_empty(self): p = TrackProfile() assert p.audio_tracks == () assert p.subtitle_tracks == () def test_with_tracks(self): a = AudioTrack(0, "eac3", 6, "5.1", "eng") s = SubtitleTrack(0, "subrip", "eng", False, False) p = TrackProfile(audio_tracks=(a,), subtitle_tracks=(s,)) assert p.audio_tracks == (a,) assert p.subtitle_tracks == (s,) def test_frozen(self): p = TrackProfile() with pytest.raises(dataclasses.FrozenInstanceError): p.audio_tracks = () # type: ignore[misc] # --------------------------------------------------------------------------- # EpisodeRelease # --------------------------------------------------------------------------- class TestEpisodeRelease: def test_construction(self): ep = _ep(1) assert ep.episodes.start == EpisodeNumber(1) assert ep.file_path == FilePath("x.mkv") def test_frozen(self): ep = _ep(1) with pytest.raises(dataclasses.FrozenInstanceError): ep.file_path = FilePath("y.mkv") # type: ignore[misc] # --------------------------------------------------------------------------- # SeasonRelease # --------------------------------------------------------------------------- class TestSeasonReleaseValidation: def test_construction_pack(self): s = SeasonRelease( season_number=SeasonNumber(1), folder="Show.S01", mode=ReleaseMode.PACK, episodes=(_ep(1), _ep(2)), ) assert s.season_number == SeasonNumber(1) assert s.mode == ReleaseMode.PACK def test_construction_episodic(self): s = SeasonRelease( season_number=SeasonNumber(2), folder="Show.S02", mode=ReleaseMode.EPISODIC, episodes=(), ) assert s.mode == ReleaseMode.EPISODIC def test_season_number_must_be_vo(self): with pytest.raises(ValidationError): SeasonRelease( season_number=1, # type: ignore[arg-type] folder="X", mode=ReleaseMode.PACK, ) def test_mode_must_be_enum(self): with pytest.raises(ValidationError): SeasonRelease( season_number=SeasonNumber(1), folder="X", mode="pack", # type: ignore[arg-type] ) def test_folder_must_be_non_empty(self): with pytest.raises(ValidationError): SeasonRelease( season_number=SeasonNumber(1), folder="", mode=ReleaseMode.PACK, ) class TestSeasonReleaseEpisodeCount: def test_zero_with_no_episodes(self): s = SeasonRelease( season_number=SeasonNumber(1), folder="X", mode=ReleaseMode.EPISODIC, episodes=(), ) assert s.episode_count() == 0 def test_all_singles(self): s = SeasonRelease( season_number=SeasonNumber(1), folder="X", mode=ReleaseMode.PACK, episodes=(_ep(1), _ep(2), _ep(3)), ) assert s.episode_count() == 3 def test_with_multi_episode_files(self): # E01 + E02-E04 + E05 -> 1 + 3 + 1 = 5 s = SeasonRelease( season_number=SeasonNumber(1), folder="X", mode=ReleaseMode.PACK, episodes=(_ep(1), _ep(2, 4), _ep(5)), ) assert s.episode_count() == 5 # --------------------------------------------------------------------------- # SeriesRelease # --------------------------------------------------------------------------- def _season(n: int, *eps_ranges: tuple[int, int]) -> SeasonRelease: eps = tuple(_ep(s, e) for s, e in eps_ranges) return SeasonRelease( season_number=SeasonNumber(n), folder=f"Show.S{n:02d}", mode=ReleaseMode.PACK, episodes=eps, ) class TestSeriesReleaseValidation: def test_construction_minimal(self): r = SeriesRelease( tmdb_id=TmdbId(84958), imdb_id=None, seasons=(), ) assert r.tmdb_id == TmdbId(84958) assert r.imdb_id is None assert r.seasons == () def test_construction_with_imdb(self): r = SeriesRelease( tmdb_id=TmdbId(84958), imdb_id=ImdbId("tt0804484"), ) assert r.imdb_id == ImdbId("tt0804484") def test_tmdb_id_must_be_vo(self): with pytest.raises(ValidationError): SeriesRelease(tmdb_id=84958, imdb_id=None) # type: ignore[arg-type] def test_imdb_id_wrong_type_raises(self): with pytest.raises(ValidationError): SeriesRelease( tmdb_id=TmdbId(84958), imdb_id="tt0804484", # type: ignore[arg-type] ) def test_duplicate_season_raises(self): with pytest.raises(ValidationError): SeriesRelease( tmdb_id=TmdbId(1), imdb_id=None, seasons=(_season(1, (1, 1)), _season(1, (2, 2))), ) class TestSeriesReleaseQueries: def test_get_season_present(self): r = SeriesRelease( tmdb_id=TmdbId(1), imdb_id=None, seasons=(_season(1, (1, 1)), _season(2, (1, 1))), ) s = r.get_season(SeasonNumber(2)) assert s is not None assert s.season_number == SeasonNumber(2) def test_get_season_absent(self): r = SeriesRelease( tmdb_id=TmdbId(1), imdb_id=None, seasons=(_season(1, (1, 1)),), ) assert r.get_season(SeasonNumber(99)) is None # --------------------------------------------------------------------------- # MovieRelease # --------------------------------------------------------------------------- class TestMovieRelease: def test_construction(self): m = MovieRelease( tmdb_id=TmdbId(27205), imdb_id=ImdbId("tt1375666"), folder="Inception.2010.1080p.BluRay.x264-GROUP", file_path=FilePath("Inception.2010.1080p.BluRay.x264-GROUP.mkv"), added_at=_ADDED_AT, ) assert m.tmdb_id == TmdbId(27205) assert m.imdb_id == ImdbId("tt1375666") assert m.added_at == _ADDED_AT def test_optional_imdb(self): m = MovieRelease( tmdb_id=TmdbId(27205), imdb_id=None, folder="X", file_path=FilePath("x.mkv"), added_at=_ADDED_AT, ) assert m.imdb_id is None def test_tmdb_id_must_be_vo(self): with pytest.raises(ValidationError): MovieRelease( tmdb_id=27205, # type: ignore[arg-type] imdb_id=None, folder="X", file_path=FilePath("x.mkv"), added_at=_ADDED_AT, ) def test_folder_must_be_non_empty(self): with pytest.raises(ValidationError): MovieRelease( tmdb_id=TmdbId(27205), imdb_id=None, folder="", file_path=FilePath("x.mkv"), added_at=_ADDED_AT, ) def test_added_at_must_be_datetime(self): with pytest.raises(ValidationError): MovieRelease( tmdb_id=TmdbId(27205), imdb_id=None, folder="X", file_path=FilePath("x.mkv"), added_at="2026-05-25", # type: ignore[arg-type] )