Files
unshackle/tests/core/test_episode_air_date.py

159 lines
5.5 KiB
Python

"""Tests for date-based ("daily"/sports) episode naming via Episode.air_date."""
from __future__ import annotations
from datetime import date, datetime
import pytest
from unshackle.core.config import config
from unshackle.core.titles.episode import Episode
class DummyService:
pass
class StubMediaInfo:
"""Minimal MediaInfo stand-in: the template context only reads track lists."""
video_tracks: list = []
audio_tracks: list = []
def make_episode(**overrides) -> Episode:
kwargs = dict(
id_="ep-0001",
service=DummyService,
title="The Show",
season=2024,
number=181,
name="The Big Race",
year=2024,
)
kwargs.update(overrides)
return Episode(**kwargs)
@pytest.fixture
def reset_template():
saved = (config.output_template, config.folder_templates, config.folder_template)
config.output_template = {"series": "{title}.{year?}.{season_episode}.{quality}-{tag}"}
config.folder_templates = {}
config.folder_template = ""
yield config
config.output_template, config.folder_templates, config.folder_template = saved
def test_air_date_replaces_season_episode_dotted(reset_template):
ep = make_episode(air_date=date(2024, 6, 30))
ctx = ep.build_template_context(StubMediaInfo())
assert ctx["season_episode"] == "2024.06.30"
assert ctx["season"] == "2024.06.30"
assert ctx["episode"] == ""
assert ctx["year"] == "" # date carries the year
assert ctx["date"] == "2024-06-30" # ISO regardless of separator
def test_air_date_uses_space_separator(reset_template):
reset_template.output_template = {"series": "{title} {season_episode} {quality}-{tag}"}
ep = make_episode(air_date=date(2024, 6, 30))
assert ep.build_template_context(StubMediaInfo())["season_episode"] == "2024 06 30"
def test_iso_string_air_date_coerced(reset_template):
ep = make_episode(air_date="2024-06-30")
assert isinstance(ep.air_date, date)
assert ep.build_template_context(StubMediaInfo())["season_episode"] == "2024.06.30"
def test_year_always_dropped_in_file_when_dated(reset_template):
"""The air date is the sole date in the filename; a distinct year is dropped too (deterministic)."""
ep = make_episode(year=2010, air_date=date(2013, 10, 30))
ctx = ep.build_template_context(StubMediaInfo())
assert ctx["year"] == ""
assert ctx["season_episode"] == "2013.10.30"
def test_yearless_title_takes_year_from_date(reset_template):
"""A title with no year still names cleanly; the date supplies the year."""
ep = make_episode(year=None, air_date=date(2026, 6, 28))
ctx = ep.build_template_context(StubMediaInfo())
assert ctx["year"] == ""
assert ctx["season_episode"] == "2026.06.28"
def test_datetime_normalized_to_date(reset_template):
"""A datetime (subclass of date) must not leak its time into the {date} token."""
ep = make_episode(air_date=datetime(2024, 6, 30, 13, 0, 0))
assert type(ep.air_date) is date
assert ep.build_template_context(StubMediaInfo())["date"] == "2024-06-30"
def test_no_air_date_is_unchanged(reset_template):
"""Regression: without air_date, naming is exactly as before (presentation-only field)."""
ep = make_episode()
ctx = ep.build_template_context(StubMediaInfo())
assert ctx["season_episode"] == "S2024E181"
assert ctx["season"] == "S2024"
assert ctx["episode"] == "E181"
assert ctx["year"] == 2024
assert ctx["date"] == ""
def test_dated_folder_groups_by_year(reset_template):
reset_template.folder_templates = {}
reset_template.folder_template = ""
folder = make_episode(air_date=date(2024, 6, 30)).get_filename(StubMediaInfo(), folder=True)
assert "2024" in folder
assert "S2024" not in folder # season folder is the year, not the faked season
def test_dated_folder_drops_year_matching_air_year(reset_template):
"""{year?} would repeat the air-year season label (Title.2024.2024); it must render once."""
folder = make_episode(air_date=date(2024, 6, 30)).get_filename(StubMediaInfo(), folder=True)
assert "2024.2024" not in folder
def test_dated_folder_keeps_distinct_year(reset_template):
"""A series year different from the air year is real information and stays."""
folder = make_episode(year=2010, air_date=date(2013, 10, 30)).get_filename(StubMediaInfo(), folder=True)
assert "2010" in folder
assert "2013" in folder
def test_a_dated_episode_matches_its_iso_date_key():
assert make_episode(air_date=date(2024, 6, 30)).matches_wanted({"2024-06-30"})
def test_a_dated_episode_still_matches_its_season_episode_key():
assert make_episode(air_date=date(2024, 6, 30)).matches_wanted({"2024x181"})
def test_a_date_key_does_not_match_another_day():
assert not make_episode(air_date=date(2024, 6, 30)).matches_wanted({"2024-07-01"})
def test_a_season_episode_exclusion_beats_the_date_key():
assert not make_episode(air_date=date(2024, 6, 30)).matches_wanted({"2024-06-30", "!2024x181"})
def test_an_undated_episode_is_unaffected_by_date_keys():
episode = make_episode()
assert not episode.matches_wanted({"2024-06-30"})
assert episode.matches_wanted({"2024x181"})
def test_a_pre_1970_air_date_is_rejected():
with pytest.raises(ValueError):
make_episode(air_date=date(1969, 12, 31))
def test_a_future_air_date_is_accepted():
future = date.today().replace(year=date.today().year + 1)
assert make_episode(air_date=future).air_date == future
def test_an_unparseable_air_date_string_is_kept_as_is():
assert make_episode(air_date="soon").air_date == "soon"