249c5de76a
- Refactor memory system (episodic/STM/LTM with components) - Implement complete subtitle domain (scanner, matcher, placer) - Add YAML workflow infrastructure - Externalize knowledge base (patterns, release groups) - Add comprehensive testing suite - Create manual testing CLIs
224 lines
6.9 KiB
Python
224 lines
6.9 KiB
Python
"""Tests for TV Show domain — entities and value objects."""
|
|
|
|
import pytest
|
|
|
|
from alfred.domain.shared.exceptions import ValidationError
|
|
from alfred.domain.tv_shows.entities import Episode, Season, TVShow
|
|
from alfred.domain.tv_shows.value_objects import EpisodeNumber, SeasonNumber, ShowStatus
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ShowStatus
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestShowStatus:
|
|
|
|
def test_from_string_ongoing(self):
|
|
assert ShowStatus.from_string("ongoing") == ShowStatus.ONGOING
|
|
|
|
def test_from_string_ended(self):
|
|
assert ShowStatus.from_string("ended") == ShowStatus.ENDED
|
|
|
|
def test_from_string_case_insensitive(self):
|
|
assert ShowStatus.from_string("ONGOING") == ShowStatus.ONGOING
|
|
assert ShowStatus.from_string("Ended") == ShowStatus.ENDED
|
|
|
|
def test_from_string_unknown(self):
|
|
assert ShowStatus.from_string("cancelled") == ShowStatus.UNKNOWN
|
|
assert ShowStatus.from_string("") == ShowStatus.UNKNOWN
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# SeasonNumber
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestSeasonNumber:
|
|
|
|
def test_valid_season(self):
|
|
s = SeasonNumber(1)
|
|
assert s.value == 1
|
|
|
|
def test_season_zero_is_specials(self):
|
|
s = SeasonNumber(0)
|
|
assert s.is_special()
|
|
|
|
def test_normal_season_not_special(self):
|
|
assert not SeasonNumber(3).is_special()
|
|
|
|
def test_negative_raises(self):
|
|
with pytest.raises(ValidationError):
|
|
SeasonNumber(-1)
|
|
|
|
def test_too_high_raises(self):
|
|
with pytest.raises(ValidationError):
|
|
SeasonNumber(101)
|
|
|
|
def test_non_integer_raises(self):
|
|
with pytest.raises((ValidationError, TypeError)):
|
|
SeasonNumber("1") # type: ignore
|
|
|
|
def test_str_and_int(self):
|
|
s = SeasonNumber(5)
|
|
assert str(s) == "5"
|
|
assert int(s) == 5
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# EpisodeNumber
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestEpisodeNumber:
|
|
|
|
def test_valid_episode(self):
|
|
e = EpisodeNumber(1)
|
|
assert e.value == 1
|
|
|
|
def test_zero_raises(self):
|
|
with pytest.raises(ValidationError):
|
|
EpisodeNumber(0)
|
|
|
|
def test_negative_raises(self):
|
|
with pytest.raises(ValidationError):
|
|
EpisodeNumber(-5)
|
|
|
|
def test_too_high_raises(self):
|
|
with pytest.raises(ValidationError):
|
|
EpisodeNumber(1001)
|
|
|
|
def test_str_and_int(self):
|
|
e = EpisodeNumber(12)
|
|
assert str(e) == "12"
|
|
assert int(e) == 12
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TVShow entity
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestTVShow:
|
|
|
|
def _make(self, imdb_id="tt0903747", title="Breaking Bad", seasons=5, status="ended"):
|
|
return TVShow(imdb_id=imdb_id, title=title, seasons_count=seasons, status=status)
|
|
|
|
def test_basic_creation(self):
|
|
show = self._make()
|
|
assert show.title == "Breaking Bad"
|
|
assert show.seasons_count == 5
|
|
|
|
def test_coerces_string_imdb_id(self):
|
|
show = self._make()
|
|
from alfred.domain.shared.value_objects import ImdbId
|
|
assert isinstance(show.imdb_id, ImdbId)
|
|
|
|
def test_coerces_string_status(self):
|
|
show = self._make(status="ongoing")
|
|
assert show.status == ShowStatus.ONGOING
|
|
|
|
def test_is_ongoing(self):
|
|
show = self._make(status="ongoing")
|
|
assert show.is_ongoing()
|
|
assert not show.is_ended()
|
|
|
|
def test_is_ended(self):
|
|
show = self._make(status="ended")
|
|
assert show.is_ended()
|
|
assert not show.is_ongoing()
|
|
|
|
def test_negative_seasons_raises(self):
|
|
with pytest.raises(ValueError):
|
|
TVShow(imdb_id="tt0903747", title="X", seasons_count=-1, status="ended")
|
|
|
|
def test_invalid_imdb_id_type_raises(self):
|
|
with pytest.raises(ValueError):
|
|
TVShow(imdb_id=12345, title="X", seasons_count=1, status="ended") # type: ignore
|
|
|
|
def test_get_folder_name_replaces_spaces(self):
|
|
show = self._make(title="Breaking Bad")
|
|
assert show.get_folder_name() == "Breaking.Bad"
|
|
|
|
def test_get_folder_name_strips_special_chars(self):
|
|
show = self._make(title="It's Always Sunny")
|
|
name = show.get_folder_name()
|
|
assert "'" not in name
|
|
|
|
def test_str_repr(self):
|
|
show = self._make()
|
|
assert "Breaking Bad" in str(show)
|
|
assert "tt0903747" in repr(show)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Season entity
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestSeason:
|
|
|
|
def test_basic_creation(self):
|
|
s = Season(show_imdb_id="tt0903747", season_number=1, episode_count=7)
|
|
assert s.episode_count == 7
|
|
|
|
def test_get_folder_name_normal(self):
|
|
s = Season(show_imdb_id="tt0903747", season_number=2, episode_count=13)
|
|
assert s.get_folder_name() == "Season 02"
|
|
|
|
def test_get_folder_name_specials(self):
|
|
s = Season(show_imdb_id="tt0903747", season_number=0, episode_count=3)
|
|
assert s.get_folder_name() == "Specials"
|
|
assert s.is_special()
|
|
|
|
def test_negative_episode_count_raises(self):
|
|
with pytest.raises(ValueError):
|
|
Season(show_imdb_id="tt0903747", season_number=1, episode_count=-1)
|
|
|
|
def test_str(self):
|
|
s = Season(show_imdb_id="tt0903747", season_number=1, episode_count=7, name="Pilot Season")
|
|
assert "Pilot Season" in str(s)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Episode entity
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestEpisode:
|
|
|
|
def test_basic_creation(self):
|
|
e = Episode(
|
|
show_imdb_id="tt0903747",
|
|
season_number=1,
|
|
episode_number=1,
|
|
title="Pilot",
|
|
)
|
|
assert e.title == "Pilot"
|
|
|
|
def test_get_filename_format(self):
|
|
e = Episode(
|
|
show_imdb_id="tt0903747",
|
|
season_number=1,
|
|
episode_number=5,
|
|
title="Gray Matter",
|
|
)
|
|
filename = e.get_filename()
|
|
assert filename.startswith("S01E05")
|
|
assert "Gray.Matter" in filename
|
|
|
|
def test_has_file_false_when_no_path(self):
|
|
e = Episode(
|
|
show_imdb_id="tt0903747",
|
|
season_number=1,
|
|
episode_number=1,
|
|
title="Pilot",
|
|
)
|
|
assert not e.has_file()
|
|
assert not e.is_downloaded()
|
|
|
|
def test_str_format(self):
|
|
e = Episode(
|
|
show_imdb_id="tt0903747",
|
|
season_number=2,
|
|
episode_number=3,
|
|
title="Bit by a Dead Bee",
|
|
)
|
|
s = str(e)
|
|
assert "S02E03" in s
|
|
assert "Bit by a Dead Bee" in s
|