Files
alfred/tests/domain/test_release_fixtures.py
T
francwa e62dc90bd1 refactor(release): make tech_string a derived property
ParsedRelease.tech_string was a stored str field re-computed in two
places (assemble() at parse time, enrich_from_probe() after the probe).
The second site was a reactive fix (e79ca46) for filename builders that
saw a stale value. Turn it into an @property so it stays in sync with
quality/source/codec by construction.

- Drop the field from the dataclass + the key from assemble()'s dict.
- Drop tech_string="" from parse_release's malformed-name fallback.
- Drop the manual recomputation at the end of enrich_from_probe.
- Inject the property into asdict() result in the fixtures runner
  (same treatment as is_season_pack).
- Update tests that passed tech_string= to the constructor; rewrite the
  TestTechString case that mutated p.tech_string manually.
2026-05-21 07:33:53 +02:00

68 lines
2.6 KiB
Python

"""Real-world release fixtures — anti-regression baseline for parse_release.
Each fixture under ``tests/fixtures/releases/<bucket>/<case>/expected.yaml``
declares a release name and the ``ParsedRelease`` fields it should produce.
Fields absent from the fixture's ``parsed`` block are not checked, so adding
new attributes to ``ParsedRelease`` never breaks existing fixtures.
The fixture's ``tree`` is materialized into a temp dir to prove the layout is
self-consistent, even though no filesystem assertions are made yet. The
``routing`` block (library / torrents / seed_hardlinks) is captured ahead of
the ``organize_media`` refactor — it will become verifiable once the planner
exists.
"""
from __future__ import annotations
from dataclasses import asdict
import pytest
from alfred.domain.release.services import parse_release
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
from tests.fixtures.releases.conftest import ReleaseFixture, discover_fixtures
_KB = YamlReleaseKnowledge()
FIXTURES = discover_fixtures()
def _fixture_param(f: ReleaseFixture) -> pytest.param:
marks = []
if f.xfail_reason:
marks.append(pytest.mark.xfail(reason=f.xfail_reason, strict=False))
return pytest.param(f, id=f.name, marks=marks)
@pytest.mark.parametrize(
"fixture",
[_fixture_param(f) for f in FIXTURES],
)
def test_parse_matches_fixture(fixture: ReleaseFixture, tmp_path) -> None:
# Materialize the tree to assert it is at least well-formed YAML +
# plausible filesystem paths. Catches typos / missing leading dirs early.
fixture.materialize(tmp_path)
parsed, _report = parse_release(fixture.release_name, _KB)
result = asdict(parsed)
# ``is_season_pack`` and ``tech_string`` are @property values —
# ``asdict()`` does not include them.
result["is_season_pack"] = parsed.is_season_pack
result["tech_string"] = parsed.tech_string
for field, expected in fixture.expected_parsed.items():
assert field in result, (
f"{fixture.name}: unknown field '{field}' in expected.parsed"
)
assert result[field] == expected, (
f"{fixture.name}: parsed.{field}"
f"expected {expected!r}, got {result[field]!r}"
)
def test_at_least_one_fixture_per_bucket() -> None:
"""Each bucket should hold at least one case once populated."""
buckets = {f.name.split("/")[0] for f in FIXTURES}
assert "easy" in buckets, "EASY bucket must have at least one fixture"
assert "shitty" in buckets, "SHITTY bucket must have at least one fixture"
assert "path_of_pain" in buckets, "PATH_OF_PAIN bucket must have at least one fixture"