Files
alfred/tests/domain/releases/test_entities.py
T
2026-05-26 21:45:11 +02:00

289 lines
8.8 KiB
Python

"""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]
)