Files
unshackle/tests/core/test_episode_air_date.py
T
imSp4rky 59f3cb4fbe feat(titles): add air_date for date-based episode naming
Optional Episode.air_date names daily/sports content by date instead of SxxExx (no template change). Presentation-only; `{year}` dropped only when it duplicates the date's year. Round-trips via export/import.
2026-06-30 15:45:57 -06:00

110 lines
3.8 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