git commit -m "feat: major architectural refactor
- 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"
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user