Files
alfred/tests/infrastructure/test_rule_repository.py
francwa 6e252d1e81 refactor(subtitles): inject default rules into SubtitleRuleSet.resolve()
aggregates.py used to call SubtitleKnowledgeBase().default_rules() via a
DEFAULT_RULES() helper, which silently pulled the infrastructure layer
(YAML loader) into the domain on every resolve.

Make the dependency explicit: resolve() now takes the default rules as
a parameter, and the caller (the ManageSubtitles use case) loads them
from the KB once and passes them in. Domain stays I/O-free.

- Drop DEFAULT_RULES helper and the SubtitleKnowledgeBase import from
  alfred/domain/subtitles/aggregates.py
- SubtitleRuleSet.resolve(default_rules: SubtitleMatchingRules)
- manage_subtitles use case passes kb.default_rules() at the call site
- Tests use a local SubtitleMatchingRules stand-in instead of relying
  on KB defaults
2026-05-19 15:10:06 +02:00

191 lines
7.1 KiB
Python

"""Tests for ``alfred.infrastructure.subtitle.rule_repository.RuleSetRepository``.
Loads/saves the SubtitleRuleSet inheritance chain from ``.alfred/`` YAML.
Coverage:
- ``TestLoad`` — no files → ``global_default``; rules.yaml override applied
on top; release_groups/{NAME}.yaml override applied;
SubtitlePreferences seeds the base when provided; full 3-level chain.
- ``TestFilterOverride`` — unknown keys discarded.
- ``TestSaveLocal`` — atomic write, merges with existing, creates .alfred/.
"""
from __future__ import annotations
from pathlib import Path
import yaml
from alfred.domain.subtitles.value_objects import SubtitleMatchingRules
from alfred.infrastructure.persistence.memory.ltm.components.subtitle_preferences import (
SubtitlePreferences,
)
from alfred.infrastructure.subtitle.rule_repository import (
RuleSetRepository,
_filter_override,
)
# Stand-in for KB defaults, injected at resolve().
_DEFAULT_RULES = SubtitleMatchingRules(
preferred_languages=["eng"],
preferred_formats=["srt"],
allowed_types=["standard"],
format_priority=["srt", "ass"],
min_confidence=0.7,
)
def _write(path: Path, data: dict) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(yaml.safe_dump(data), encoding="utf-8")
# --------------------------------------------------------------------------- #
# _filter_override #
# --------------------------------------------------------------------------- #
class TestFilterOverride:
def test_keeps_only_valid_keys(self):
out = _filter_override(
{
"languages": ["fra"],
"formats": ["srt"],
"types": ["standard"],
"format_priority": ["srt"],
"min_confidence": 0.8,
"unknown_key": "ignored",
"another": 42,
}
)
assert set(out) == {
"languages",
"formats",
"types",
"format_priority",
"min_confidence",
}
assert "unknown_key" not in out
def test_empty(self):
assert _filter_override({}) == {}
# --------------------------------------------------------------------------- #
# load #
# --------------------------------------------------------------------------- #
class TestLoad:
def test_no_files_returns_global_default(self, tmp_path):
repo = RuleSetRepository(tmp_path)
rs = repo.load()
# With no overrides, resolve returns the injected defaults unchanged.
rules = rs.resolve(_DEFAULT_RULES)
assert rules.preferred_languages == _DEFAULT_RULES.preferred_languages
assert rules.min_confidence == _DEFAULT_RULES.min_confidence
def test_subtitle_preferences_override_base(self, tmp_path):
prefs = SubtitlePreferences(
languages=["jpn"], formats=["ass"], types=["standard"]
)
repo = RuleSetRepository(tmp_path)
rules = repo.load(subtitle_preferences=prefs).resolve(_DEFAULT_RULES)
assert rules.preferred_languages == ["jpn"]
assert rules.preferred_formats == ["ass"]
assert rules.allowed_types == ["standard"]
def test_local_rules_yaml_applied(self, tmp_path):
_write(
tmp_path / ".alfred" / "rules.yaml",
{"override": {"languages": ["spa"], "min_confidence": 0.95}},
)
repo = RuleSetRepository(tmp_path)
rules = repo.load().resolve(_DEFAULT_RULES)
assert rules.preferred_languages == ["spa"]
assert rules.min_confidence == 0.95
def test_release_group_override_applied(self, tmp_path):
_write(
tmp_path / ".alfred" / "release_groups" / "KONTRAST.yaml",
{"override": {"format_priority": ["ass", "srt"]}},
)
repo = RuleSetRepository(tmp_path)
rules = repo.load(release_group="KONTRAST").resolve(_DEFAULT_RULES)
assert rules.format_priority == ["ass", "srt"]
def test_full_three_level_chain(self, tmp_path):
# Base: prefs sets languages=["jpn"]
prefs = SubtitlePreferences(languages=["jpn"])
# Group: overrides format_priority
_write(
tmp_path / ".alfred" / "release_groups" / "GRP.yaml",
{"override": {"format_priority": ["ass"]}},
)
# Local: overrides min_confidence
_write(
tmp_path / ".alfred" / "rules.yaml",
{"override": {"min_confidence": 0.99}},
)
repo = RuleSetRepository(tmp_path)
rules = repo.load(release_group="GRP", subtitle_preferences=prefs).resolve(
_DEFAULT_RULES
)
# All three levels visible — local overrides on top
assert rules.preferred_languages == ["jpn"]
assert rules.format_priority == ["ass"]
assert rules.min_confidence == 0.99
def test_release_group_yaml_without_override_section_ignored(self, tmp_path):
_write(
tmp_path / ".alfred" / "release_groups" / "GRP.yaml",
{"name": "GRP"}, # no 'override' key
)
# Must not crash and must not introduce an intermediate node.
repo = RuleSetRepository(tmp_path)
rs = repo.load(release_group="GRP")
# No extra rule set was created → it's still the global default.
assert rs.scope.level == "global"
def test_missing_release_group_file_silently_ignored(self, tmp_path):
repo = RuleSetRepository(tmp_path)
rs = repo.load(release_group="DOES_NOT_EXIST")
assert rs.scope.level == "global"
# --------------------------------------------------------------------------- #
# save_local #
# --------------------------------------------------------------------------- #
class TestSaveLocal:
def test_creates_file(self, tmp_path):
repo = RuleSetRepository(tmp_path)
repo.save_local({"languages": ["spa"]})
path = tmp_path / ".alfred" / "rules.yaml"
assert path.is_file()
loaded = yaml.safe_load(path.read_text())
assert loaded == {"override": {"languages": ["spa"]}}
def test_merges_with_existing(self, tmp_path):
repo = RuleSetRepository(tmp_path)
repo.save_local({"languages": ["spa"]})
repo.save_local({"min_confidence": 0.8})
loaded = yaml.safe_load((tmp_path / ".alfred" / "rules.yaml").read_text())
assert loaded["override"]["languages"] == ["spa"]
assert loaded["override"]["min_confidence"] == 0.8
def test_overwrites_existing_key(self, tmp_path):
repo = RuleSetRepository(tmp_path)
repo.save_local({"languages": ["spa"]})
repo.save_local({"languages": ["jpn"]})
loaded = yaml.safe_load((tmp_path / ".alfred" / "rules.yaml").read_text())
assert loaded["override"]["languages"] == ["jpn"]
def test_temp_file_cleaned_up(self, tmp_path):
repo = RuleSetRepository(tmp_path)
repo.save_local({"languages": ["spa"]})
# No stale .tmp file
assert not (tmp_path / ".alfred" / "rules.yaml.tmp").exists()