3737f66851
Replace the ~480-line legacy heuristic block in services.py with a small dict-driven pass in pipeline._annotate_shitty: each token is looked up against the kb buckets (resolutions / sources / codecs / distributors / year / sxxexx) with first-match-wins semantics, the leftmost contiguous UNKNOWN run becomes the title, done. SHITTY's scope is intentionally narrow — releases that *look* like scene names but don't have a registered group schema. Anything more exotic (parenthesized tech, bare-dashed title fragments, YT slugs, franchise boxes) is PATH OF PAIN territory and stays out of here. - annotate() no longer returns None; SHITTY is the always-on fallback - services.py shrunk from ~525 to ~85 lines (legacy extractors gone) - 4 fixtures get xfail markers documenting PoP-grade pathologies (deutschland franchise box, sleaford YT slug, super_mario bilingual, predator space-separators — the last one moved from shitty/ → pop/) - ReleaseFixture grows xfail_reason; the parametrized suite wires the pytest.mark.xfail(strict=False) automatically
69 lines
2.1 KiB
Python
69 lines
2.1 KiB
Python
"""Fixture discovery and materialization helpers for release fixtures.
|
|
|
|
Each fixture is a directory under ``tests/fixtures/releases/<bucket>/<case>/``
|
|
containing one ``expected.yaml`` file. See ``releases/README.md`` for the
|
|
schema.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
|
|
import yaml
|
|
|
|
FIXTURES_ROOT = Path(__file__).parent
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ReleaseFixture:
|
|
"""A loaded fixture, ready to be materialized into a temp dir."""
|
|
|
|
name: str # "<bucket>/<case>", e.g. "easy/back_in_action"
|
|
path: Path # directory containing expected.yaml
|
|
data: dict # parsed YAML contents
|
|
|
|
@property
|
|
def release_name(self) -> str:
|
|
return self.data["release_name"]
|
|
|
|
@property
|
|
def expected_parsed(self) -> dict:
|
|
return self.data.get("parsed", {})
|
|
|
|
@property
|
|
def tree(self) -> list[str]:
|
|
return self.data.get("tree", [])
|
|
|
|
@property
|
|
def routing(self) -> dict:
|
|
return self.data.get("routing", {})
|
|
|
|
@property
|
|
def xfail_reason(self) -> str | None:
|
|
"""If set, the fixture is expected to fail — wrapped with
|
|
``pytest.mark.xfail`` by the test runner. Used for known
|
|
not-supported pathological cases (typically PATH OF PAIN bucket).
|
|
"""
|
|
return self.data.get("xfail_reason")
|
|
|
|
def materialize(self, root: Path) -> None:
|
|
"""Create the fixture's ``tree`` as empty files/dirs under ``root``."""
|
|
for entry in self.tree:
|
|
target = root / entry
|
|
if entry.endswith("/"):
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
else:
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.touch()
|
|
|
|
|
|
def discover_fixtures() -> list[ReleaseFixture]:
|
|
"""Find all ``expected.yaml`` files under FIXTURES_ROOT."""
|
|
fixtures = []
|
|
for yaml_path in sorted(FIXTURES_ROOT.glob("*/*/expected.yaml")):
|
|
data = yaml.safe_load(yaml_path.read_text())
|
|
name = f"{yaml_path.parent.parent.name}/{yaml_path.parent.name}"
|
|
fixtures.append(ReleaseFixture(name=name, path=yaml_path.parent, data=data))
|
|
return fixtures
|