refactor(tv_shows): freeze aggregate, builder-only construction, drop ShowTracker fields

The TVShow aggregate is now fully immutable. TVShow, Season and Episode
are @dataclass(frozen=True), children stored as ordered tuples sorted
by number. All construction goes through TVShowBuilder / SeasonBuilder
(new module), which expose from_existing() to seed from a current
frozen aggregate and apply modifications.

ShowTracker-territory fields are stripped from the domain: ShowStatus,
CollectionStatus, expected_seasons/episodes, aired_episodes,
collection_status(), is_complete_series(), missing_episodes(),
is_ongoing(), is_ended(), Season.name, the aired<=expected validation,
and the TMDB status string mapping. These will reappear in a dedicated
ShowTracker layer (to be designed) combining the .alfred sidecar with
live TMDB data.

New SeasonMode enum (PACK / EPISODIC) computed at read time from the
season's structural shape — never stored, the YAML sidecar encodes the
mode via presence/absence of the episodes: block.

Test suite for the domain entirely rewritten to cover frozen invariants,
builder ordering, last-write-wins, from_existing round-trip, and
SeasonMode derivation. Full suite still green (1078 passed).
This commit is contained in:
2026-05-22 16:09:37 +02:00
parent 1427c8a54b
commit 6c12c18a27
7 changed files with 667 additions and 486 deletions
+290 -211
View File
@@ -1,76 +1,45 @@
"""Tests for the TV Show domain — entities, value objects, aggregate behavior.
"""Tests for the TV Show domain — entities, value objects, builders.
Rewritten for the post-refactor aggregate:
Post-2026-05-22 refactor:
* ``TVShow`` is the root, owning ``seasons: dict[SeasonNumber, Season]``.
* ``Season`` owns ``episodes: dict[EpisodeNumber, Episode]`` and tracks
``expected_episodes`` + ``aired_episodes``.
* ``Episode`` carries ``audio_tracks`` + ``subtitle_tracks`` and exposes
language helpers following contract C+ (``str`` direct compare, ``Language``
cross-format).
* No back-references on Season/Episode — they are reached through the root.
* Sole sanctioned mutation entry point: ``TVShow.add_episode(ep)``.
* The aggregate is **frozen all the way** — ``TVShow``, ``Season`` and
``Episode`` are all ``@dataclass(frozen=True)``. Children are stored as
ordered tuples sorted by number.
* Construction goes exclusively through :class:`TVShowBuilder` (and its
helper :class:`SeasonBuilder`). No more ``add_episode`` / ``add_season``
on entities.
* ShowTracker-territory fields (production status, expected vs aired
counts, collection completeness) are removed from the domain. The
aggregate carries only what the ``.alfred`` sidecar stores.
Coverage:
* ``TestShowStatus`` — including the extended TMDB string mapping.
* ``TestSeasonNumber`` / ``TestEpisodeNumber`` — value-object validation.
* ``TestSeasonMode`` — enum sanity (computed in ``TestSeason``).
* ``TestEpisode`` — basic shape, file presence, audio/subtitle helpers.
* ``TestSeason`` — episode insertion, completeness vs aired, missing list.
* ``TestTVShow`` — aggregate invariants, ``add_episode``, ``collection_status``,
``missing_episodes``, ``is_complete_series``.
* ``TestSeason`` — frozen shape, episode lookup, mode derivation.
* ``TestTVShow`` — frozen aggregate root, season lookup, counts.
* ``TestSeasonBuilder`` / ``TestTVShowBuilder`` — sole sanctioned mutation
surface; ordering, last-write-wins, ``from_existing`` round-trip.
"""
from __future__ import annotations
import dataclasses
import pytest
from alfred.domain.shared.exceptions import ValidationError
from alfred.domain.shared.media import AudioTrack, SubtitleTrack
from alfred.domain.shared.value_objects import ImdbId, Language
from alfred.domain.tv_shows.builders import SeasonBuilder, TVShowBuilder
from alfred.domain.tv_shows.entities import Episode, Season, TVShow
from alfred.domain.tv_shows.value_objects import (
CollectionStatus,
EpisodeNumber,
SeasonMode,
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
@pytest.mark.parametrize(
"raw,expected",
[
("Returning Series", ShowStatus.ONGOING),
("In Production", ShowStatus.ONGOING),
("Pilot", ShowStatus.ONGOING),
("Planned", ShowStatus.ONGOING),
("Canceled", ShowStatus.ENDED),
("Cancelled", ShowStatus.ENDED),
],
)
def test_from_string_tmdb_mappings(self, raw, expected):
assert ShowStatus.from_string(raw) == expected
def test_from_string_empty_or_unknown(self):
assert ShowStatus.from_string("") == ShowStatus.UNKNOWN
assert ShowStatus.from_string("borked") == ShowStatus.UNKNOWN
# ---------------------------------------------------------------------------
# SeasonNumber
# ---------------------------------------------------------------------------
@@ -131,6 +100,17 @@ class TestEpisodeNumber:
assert int(e) == 12
# ---------------------------------------------------------------------------
# SeasonMode
# ---------------------------------------------------------------------------
class TestSeasonMode:
def test_values(self):
assert SeasonMode.PACK.value == "pack"
assert SeasonMode.EPISODIC.value == "episodic"
# ---------------------------------------------------------------------------
# Episode entity
# ---------------------------------------------------------------------------
@@ -151,6 +131,11 @@ class TestEpisode:
assert isinstance(e.season_number, SeasonNumber)
assert isinstance(e.episode_number, EpisodeNumber)
def test_is_frozen(self):
e = self._ep()
with pytest.raises(dataclasses.FrozenInstanceError):
e.title = "Other" # type: ignore[misc]
def test_get_filename_format(self):
e = self._ep(season=1, episode=5, title="Gray Matter")
filename = e.get_filename()
@@ -171,10 +156,10 @@ class TestEpisode:
def test_has_audio_in_with_str(self):
e = self._ep(
audio_tracks=[
audio_tracks=(
AudioTrack(0, "eac3", 6, "5.1", "eng"),
AudioTrack(1, "ac3", 6, "5.1", "fre"),
]
)
)
assert e.has_audio_in("eng") is True
assert e.has_audio_in("ENG") is True # case-insensitive
@@ -187,49 +172,49 @@ class TestEpisode:
native_name="Français",
aliases=("fr", "fra", "french"),
)
e = self._ep(audio_tracks=[AudioTrack(0, "ac3", 6, "5.1", "fr")])
e = self._ep(audio_tracks=(AudioTrack(0, "ac3", 6, "5.1", "fr"),))
# str query "fre" wouldn't match "fr" directly — but Language does cross-format
assert e.has_audio_in(lang) is True
assert e.has_audio_in("fre") is False # direct compare misses
def test_audio_languages_dedup_in_order(self):
e = self._ep(
audio_tracks=[
audio_tracks=(
AudioTrack(0, "ac3", 6, "5.1", "eng"),
AudioTrack(1, "ac3", 6, "5.1", "fre"),
AudioTrack(2, "aac", 2, "stereo", "eng"), # dupe
AudioTrack(3, "aac", 2, "stereo", None), # skipped
]
)
)
assert e.audio_languages() == ["eng", "fre"]
# ── Subtitle helpers ───────────────────────────────────────────────
def test_has_subtitles_in(self):
e = self._ep(subtitle_tracks=[SubtitleTrack(0, "subrip", "fre")])
e = self._ep(subtitle_tracks=(SubtitleTrack(0, "subrip", "fre"),))
assert e.has_subtitles_in("fre") is True
assert e.has_subtitles_in("eng") is False
def test_has_forced_subs(self):
e = self._ep(
subtitle_tracks=[
subtitle_tracks=(
SubtitleTrack(0, "subrip", "eng", is_forced=False),
SubtitleTrack(1, "subrip", "eng", is_forced=True),
]
)
)
assert e.has_forced_subs() is True
def test_has_forced_subs_false_when_none(self):
e = self._ep(subtitle_tracks=[SubtitleTrack(0, "subrip", "eng")])
e = self._ep(subtitle_tracks=(SubtitleTrack(0, "subrip", "eng"),))
assert e.has_forced_subs() is False
def test_subtitle_languages_dedup_in_order(self):
e = self._ep(
subtitle_tracks=[
subtitle_tracks=(
SubtitleTrack(0, "subrip", "eng"),
SubtitleTrack(1, "subrip", "fre"),
SubtitleTrack(2, "subrip", "eng"),
]
)
)
assert e.subtitle_languages() == ["eng", "fre"]
@@ -247,7 +232,12 @@ class TestSeason:
s = Season(season_number=1)
assert isinstance(s.season_number, SeasonNumber)
assert s.episode_count == 0
assert s.episodes == {}
assert s.episodes == ()
def test_is_frozen(self):
s = Season(season_number=1)
with pytest.raises(dataclasses.FrozenInstanceError):
s.episodes = (self._ep(1),) # type: ignore[misc]
def test_get_folder_name_normal(self):
assert Season(season_number=2).get_folder_name() == "Season 02"
@@ -257,82 +247,34 @@ class TestSeason:
assert s.get_folder_name() == "Specials"
assert s.is_special()
def test_negative_aired_raises(self):
with pytest.raises(ValueError):
Season(season_number=1, aired_episodes=-1)
# ── Mode derivation ────────────────────────────────────────────────
def test_aired_cannot_exceed_expected(self):
with pytest.raises(ValueError):
Season(season_number=1, expected_episodes=5, aired_episodes=6)
def test_add_episode_rejects_mismatched_season(self):
def test_mode_pack_when_no_episodes(self):
s = Season(season_number=1)
ep = Episode(season_number=2, episode_number=1, title="x")
with pytest.raises(ValueError):
s.add_episode(ep)
assert s.mode == SeasonMode.PACK
def test_add_episode_replaces_same_number(self):
s = Season(season_number=1)
s.add_episode(self._ep(1))
s.add_episode(Episode(season_number=1, episode_number=1, title="Replaced"))
assert s.episodes[EpisodeNumber(1)].title == "Replaced"
def test_mode_episodic_when_episodes_present(self):
s = Season(season_number=1, episodes=(self._ep(1),))
assert s.mode == SeasonMode.EPISODIC
def test_str_uses_name_when_present(self):
s = Season(season_number=1, name="Pilot Season")
assert "Pilot Season" in str(s)
# ── Episode access ─────────────────────────────────────────────────
# ── Completeness vs aired ──────────────────────────────────────────
def test_get_episode_returns_match(self):
ep1 = self._ep(1)
ep2 = self._ep(2)
s = Season(season_number=1, episodes=(ep1, ep2))
assert s.get_episode(EpisodeNumber(2)) is ep2
def test_is_complete_unknown_aired_is_false(self):
# Conservative: no aired count → cannot claim complete
s = Season(season_number=1)
s.add_episode(self._ep(1))
assert s.is_complete() is False
def test_get_episode_returns_none_when_absent(self):
s = Season(season_number=1, episodes=(self._ep(1),))
assert s.get_episode(EpisodeNumber(99)) is None
def test_is_complete_when_owning_all_aired(self):
s = Season(season_number=1, aired_episodes=3)
for i in (1, 2, 3):
s.add_episode(self._ep(i))
assert s.is_complete() is True
def test_is_complete_zero_aired_is_trivially_true(self):
s = Season(season_number=1, aired_episodes=0)
assert s.is_complete() is True
def test_partial_when_missing_aired_episodes(self):
s = Season(season_number=1, aired_episodes=3)
s.add_episode(self._ep(1))
assert s.is_complete() is False
def test_is_fully_aired(self):
s = Season(season_number=1, expected_episodes=10, aired_episodes=10)
assert s.is_fully_aired() is True
def test_is_fully_aired_false_when_in_flight(self):
s = Season(season_number=1, expected_episodes=10, aired_episodes=4)
assert s.is_fully_aired() is False
def test_is_fully_aired_false_with_unknowns(self):
assert Season(season_number=1).is_fully_aired() is False
def test_missing_episodes_when_partial(self):
s = Season(season_number=1, aired_episodes=5)
s.add_episode(self._ep(1))
s.add_episode(self._ep(3))
missing = [n.value for n in s.missing_episodes()]
assert missing == [2, 4, 5]
def test_missing_episodes_empty_when_complete(self):
s = Season(season_number=1, aired_episodes=2)
s.add_episode(self._ep(1))
s.add_episode(self._ep(2))
assert s.missing_episodes() == []
def test_missing_episodes_empty_when_unknown_aired(self):
# Without an aired count we cannot reason about gaps
s = Season(season_number=1)
s.add_episode(self._ep(2))
assert s.missing_episodes() == []
def test_episode_count_reflects_tuple_size(self):
s = Season(
season_number=1,
episodes=(self._ep(1), self._ep(2), self._ep(3)),
)
assert s.episode_count == 3
# ---------------------------------------------------------------------------
@@ -342,40 +284,30 @@ class TestSeason:
class TestTVShow:
def _show(self, **kwargs) -> TVShow:
defaults = dict(
imdb_id="tt0903747",
title="Breaking Bad",
status="ended",
)
defaults = dict(imdb_id="tt0903747", title="Breaking Bad")
defaults.update(kwargs)
return TVShow(**defaults)
# ── Construction & coercion ────────────────────────────────────────
def test_basic_creation(self):
show = self._show(expected_seasons=5)
show = self._show()
assert show.title == "Breaking Bad"
assert show.expected_seasons == 5
assert show.seasons == {}
assert show.seasons == ()
assert show.seasons_count == 0
assert show.episode_count == 0
def test_coerces_string_imdb_id(self):
assert isinstance(self._show().imdb_id, ImdbId)
def test_coerces_string_status(self):
assert self._show(status="ongoing").status == ShowStatus.ONGOING
def test_is_ongoing_and_is_ended(self):
assert self._show(status="ongoing").is_ongoing()
assert self._show(status="ended").is_ended()
def test_negative_expected_seasons_raises(self):
with pytest.raises(ValueError):
self._show(expected_seasons=-1)
def test_invalid_imdb_id_type_raises(self):
with pytest.raises(ValueError):
TVShow(imdb_id=12345, title="X", status="ended") # type: ignore
TVShow(imdb_id=12345, title="X") # type: ignore
def test_is_frozen(self):
show = self._show()
with pytest.raises(dataclasses.FrozenInstanceError):
show.title = "Other" # type: ignore[misc]
def test_get_folder_name_replaces_spaces(self):
assert self._show(title="Breaking Bad").get_folder_name() == "Breaking.Bad"
@@ -389,77 +321,224 @@ class TestTVShow:
assert "Breaking Bad" in str(show)
assert "tt0903747" in repr(show)
# ── add_episode — the only sanctioned mutation ─────────────────────
# ── Season access ──────────────────────────────────────────────────
def test_get_season_returns_match(self):
s1 = Season(season_number=1)
s2 = Season(season_number=2)
show = self._show(seasons=(s1, s2))
assert show.get_season(SeasonNumber(2)) is s2
def test_get_season_returns_none_when_absent(self):
show = self._show(seasons=(Season(season_number=1),))
assert show.get_season(SeasonNumber(99)) is None
def test_episode_count_aggregates_across_seasons(self):
ep11 = Episode(season_number=1, episode_number=1, title="x")
ep12 = Episode(season_number=1, episode_number=2, title="y")
ep21 = Episode(season_number=2, episode_number=1, title="z")
show = self._show(
seasons=(
Season(season_number=1, episodes=(ep11, ep12)),
Season(season_number=2, episodes=(ep21,)),
)
)
assert show.episode_count == 3
assert show.seasons_count == 2
def test_tmdb_id_defaults_to_none(self):
assert self._show().tmdb_id is None
# ---------------------------------------------------------------------------
# SeasonBuilder
# ---------------------------------------------------------------------------
class TestSeasonBuilder:
def _ep(self, episode: int) -> Episode:
return Episode(season_number=1, episode_number=episode, title=f"Ep {episode}")
def test_build_empty(self):
s = SeasonBuilder(SeasonNumber(1)).build()
assert isinstance(s, Season)
assert s.episodes == ()
assert s.mode == SeasonMode.PACK
def test_build_emits_sorted_episodes(self):
s = (
SeasonBuilder(SeasonNumber(1))
.add_episode(self._ep(3))
.add_episode(self._ep(1))
.add_episode(self._ep(2))
.build()
)
assert [ep.episode_number.value for ep in s.episodes] == [1, 2, 3]
def test_add_episode_last_write_wins(self):
first = Episode(season_number=1, episode_number=1, title="First")
second = Episode(season_number=1, episode_number=1, title="Replacement")
s = (
SeasonBuilder(SeasonNumber(1))
.add_episode(first)
.add_episode(second)
.build()
)
assert s.episodes == (second,)
assert s.episodes[0].title == "Replacement"
def test_add_episode_rejects_mismatched_season(self):
builder = SeasonBuilder(SeasonNumber(1))
with pytest.raises(ValueError):
builder.add_episode(
Episode(season_number=2, episode_number=1, title="bad")
)
def test_int_season_number_coerced(self):
s = SeasonBuilder(1).build()
assert s.season_number == SeasonNumber(1)
def test_from_existing_round_trip(self):
original = Season(
season_number=1,
episodes=(self._ep(1), self._ep(2)),
)
rebuilt = SeasonBuilder.from_existing(original).build()
assert rebuilt == original
def test_from_existing_then_add_replaces(self):
original = Season(season_number=1, episodes=(self._ep(1), self._ep(2)))
replacement = Episode(season_number=1, episode_number=2, title="New")
rebuilt = (
SeasonBuilder.from_existing(original).add_episode(replacement).build()
)
assert rebuilt.get_episode(EpisodeNumber(2)) is replacement
# ---------------------------------------------------------------------------
# TVShowBuilder
# ---------------------------------------------------------------------------
class TestTVShowBuilder:
def _ep(self, season: int, episode: int) -> Episode:
return Episode(
season_number=season,
episode_number=episode,
title=f"S{season:02d}E{episode:02d}",
)
def test_build_minimal(self):
show = TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad").build()
assert isinstance(show, TVShow)
assert show.title == "Breaking Bad"
assert show.seasons == ()
assert show.tmdb_id is None
def test_coerces_string_imdb_id(self):
show = TVShowBuilder(imdb_id="tt0903747", title="x").build()
assert isinstance(show.imdb_id, ImdbId)
def test_add_episode_creates_missing_season(self):
show = self._show()
show.add_episode(Episode(season_number=1, episode_number=1, title="Pilot"))
assert SeasonNumber(1) in show.seasons
show = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_episode(self._ep(1, 1))
.build()
)
assert show.seasons_count == 1
assert show.get_season(SeasonNumber(1)) is not None
assert show.episode_count == 1
def test_add_episode_reuses_existing_season(self):
show = self._show()
show.add_episode(Episode(season_number=1, episode_number=1, title="A"))
show.add_episode(Episode(season_number=1, episode_number=2, title="B"))
show = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_episode(self._ep(1, 1))
.add_episode(self._ep(1, 2))
.build()
)
assert show.seasons_count == 1
assert show.episode_count == 2
def test_seasons_emitted_sorted(self):
show = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_episode(self._ep(3, 1))
.add_episode(self._ep(1, 1))
.add_episode(self._ep(2, 1))
.build()
)
assert [s.season_number.value for s in show.seasons] == [1, 2, 3]
def test_episodes_within_season_emitted_sorted(self):
show = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_episode(self._ep(1, 3))
.add_episode(self._ep(1, 1))
.add_episode(self._ep(1, 2))
.build()
)
season = show.get_season(SeasonNumber(1))
assert season is not None
assert [ep.episode_number.value for ep in season.episodes] == [1, 2, 3]
def test_add_season_replaces_existing(self):
show = self._show()
s1 = Season(season_number=1, aired_episodes=10)
show.add_season(s1)
s1bis = Season(season_number=1, aired_episodes=5)
show.add_season(s1bis)
assert show.seasons[SeasonNumber(1)] is s1bis
first = Season(season_number=1, episodes=(self._ep(1, 1),))
second = Season(
season_number=1, episodes=(self._ep(1, 5), self._ep(1, 6))
)
show = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_season(first)
.add_season(second)
.build()
)
season = show.get_season(SeasonNumber(1))
assert season is not None
assert [ep.episode_number.value for ep in season.episodes] == [5, 6]
# ── Collection status ──────────────────────────────────────────────
def test_season_builder_returns_same_instance(self):
builder = TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
sb1 = builder.season_builder(1)
sb2 = builder.season_builder(1)
assert sb1 is sb2
def test_collection_status_empty(self):
assert self._show().collection_status() == CollectionStatus.EMPTY
def test_season_builder_via_int(self):
builder = TVShowBuilder(imdb_id="tt0903747", title="x")
sb = builder.season_builder(5)
assert sb.season_number == SeasonNumber(5)
def test_collection_status_partial_missing_episode(self):
show = self._show()
s = Season(season_number=1, aired_episodes=3)
s.add_episode(Episode(season_number=1, episode_number=1, title="x"))
show.add_season(s)
assert show.collection_status() == CollectionStatus.PARTIAL
def test_set_title_and_tmdb_id(self):
show = (
TVShowBuilder(imdb_id="tt0903747", title="Initial")
.set_title("Updated")
.set_tmdb_id(1396)
.build()
)
assert show.title == "Updated"
assert show.tmdb_id == 1396
def test_collection_status_complete(self):
show = self._show(expected_seasons=1)
s = Season(season_number=1, aired_episodes=2)
for n in (1, 2):
s.add_episode(Episode(season_number=1, episode_number=n, title=f"e{n}"))
show.add_season(s)
assert show.collection_status() == CollectionStatus.COMPLETE
def test_from_existing_round_trip(self):
original = (
TVShowBuilder(
imdb_id="tt0903747",
title="Breaking Bad",
tmdb_id=1396,
)
.add_episode(self._ep(1, 1))
.add_episode(self._ep(2, 1))
.build()
)
rebuilt = TVShowBuilder.from_existing(original).build()
assert rebuilt == original
def test_collection_status_partial_when_seasons_missing(self):
# Seasons we own are complete, but expected_seasons says more exist.
show = self._show(expected_seasons=2)
s = Season(season_number=1, aired_episodes=1)
s.add_episode(Episode(season_number=1, episode_number=1, title="x"))
show.add_season(s)
assert show.collection_status() == CollectionStatus.PARTIAL
def test_is_complete_series_requires_ended_and_complete(self):
show = self._show(status="ongoing", expected_seasons=1)
s = Season(season_number=1, aired_episodes=1)
s.add_episode(Episode(season_number=1, episode_number=1, title="x"))
show.add_season(s)
# Ongoing → never "complete series" even if collection is COMPLETE
assert show.is_complete_series() is False
show.status = ShowStatus.ENDED
assert show.is_complete_series() is True
# ── missing_episodes traversal ─────────────────────────────────────
def test_missing_episodes_walks_seasons_in_order(self):
show = self._show()
s2 = Season(season_number=2, aired_episodes=2)
s1 = Season(season_number=1, aired_episodes=3)
s1.add_episode(Episode(season_number=1, episode_number=2, title="x"))
show.add_season(s2)
show.add_season(s1)
missing = [(s.value, e.value) for s, e in show.missing_episodes()]
assert missing == [(1, 1), (1, 3), (2, 1), (2, 2)]
def test_from_existing_then_add_extends(self):
original = (
TVShowBuilder(imdb_id="tt0903747", title="Breaking Bad")
.add_episode(self._ep(1, 1))
.build()
)
extended = (
TVShowBuilder.from_existing(original).add_episode(self._ep(1, 2)).build()
)
assert extended.episode_count == 2
assert original.episode_count == 1 # original untouched
@@ -23,7 +23,6 @@ from alfred.domain.tv_shows.entities import Episode, TVShow
from alfred.domain.tv_shows.value_objects import (
EpisodeNumber,
SeasonNumber,
ShowStatus,
)
from alfred.infrastructure.filesystem.filesystem_operations import (
create_folder,
@@ -171,8 +170,6 @@ def _show() -> TVShow:
return TVShow(
imdb_id=ImdbId("tt0773262"),
title="Dexter",
expected_seasons=8,
status=ShowStatus.ENDED,
)