test(release): adapt suite to explicit ReleaseKnowledge injection

- test_release.py / test_release_fixtures.py: module-level
  _KB = YamlReleaseKnowledge() + thin _parse(name) helper threading it
  into parse_release. test_show_folder_name_strips_windows_chars renamed
  to test_show_folder_name_uses_already_safe_title to reflect the
  Option B contract (caller sanitizes via kb.sanitize_for_fs).
- test_detect_media_type.py: same _KB pattern, all
  detect_media_type(parsed, path) calls now pass kb.
- test_filesystem_extras.py: find_video_file(path) calls now pass kb.
- test_enrich_from_probe.py: _bare() helper adds the new
  title_sanitized field.
- test_resolve_destination.py: drop _sanitize import + TestSanitize
  class (helper deleted), add tmdb_title_safe arg to
  _resolve_series_folder calls.

987 passed, 8 skipped.
This commit is contained in:
2026-05-19 22:05:26 +02:00
parent bf37a9d09e
commit 6802933acd
6 changed files with 81 additions and 72 deletions
+22 -19
View File
@@ -20,16 +20,19 @@ import pytest
from alfred.application.filesystem.detect_media_type import detect_media_type
from alfred.domain.release.services import parse_release
from alfred.infrastructure.knowledge.release_kb import YamlReleaseKnowledge
_KB = YamlReleaseKnowledge()
def _parsed(media_type: str = "movie"):
"""Build a ParsedRelease with the requested media_type via the real parser."""
if media_type == "tv_show":
return parse_release("Show.S01E01.1080p-GRP")
return parse_release("Show.S01E01.1080p-GRP", _KB)
if media_type == "movie":
return parse_release("Movie.2020.1080p-GRP")
return parse_release("Movie.2020.1080p-GRP", _KB)
# "unknown" / other — feed a name the parser can't classify
return parse_release("randomthing")
return parse_release("randomthing", _KB)
# --------------------------------------------------------------------------- #
@@ -41,30 +44,30 @@ class TestFile:
def test_video_file_preserves_parsed_type(self, tmp_path: Path):
f = tmp_path / "x.mkv"
f.write_bytes(b"")
assert detect_media_type(_parsed("movie"), f) == "movie"
assert detect_media_type(_parsed("movie"), f, _KB) == "movie"
def test_video_file_preserves_tv_type(self, tmp_path: Path):
f = tmp_path / "ep.mp4"
f.write_bytes(b"")
assert detect_media_type(_parsed("tv_show"), f) == "tv_show"
assert detect_media_type(_parsed("tv_show"), f, _KB) == "tv_show"
def test_non_video_file_returns_other(self, tmp_path: Path):
f = tmp_path / "x.iso"
f.write_bytes(b"")
assert detect_media_type(_parsed("movie"), f) == "other"
assert detect_media_type(_parsed("movie"), f, _KB) == "other"
@pytest.mark.parametrize("ext", [".rar", ".zip", ".7z", ".exe", ".dmg"])
def test_various_non_video_extensions(self, tmp_path: Path, ext):
f = tmp_path / f"x{ext}"
f.write_bytes(b"")
assert detect_media_type(_parsed("movie"), f) == "other"
assert detect_media_type(_parsed("movie"), f, _KB) == "other"
def test_metadata_only_file_keeps_parsed_type(self, tmp_path: Path):
# Metadata extension is stripped from conclusive set — no video, no
# non-video → falls through to parsed.media_type.
f = tmp_path / "x.nfo"
f.write_bytes(b"")
assert detect_media_type(_parsed("movie"), f) == "movie"
assert detect_media_type(_parsed("movie"), f, _KB) == "movie"
# --------------------------------------------------------------------------- #
@@ -75,27 +78,27 @@ class TestFile:
class TestFolder:
def test_folder_with_video_keeps_parsed_type(self, tmp_path: Path):
(tmp_path / "main.mkv").write_bytes(b"")
assert detect_media_type(_parsed("movie"), tmp_path) == "movie"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "movie"
def test_folder_only_non_video_returns_other(self, tmp_path: Path):
(tmp_path / "disc.iso").write_bytes(b"")
(tmp_path / "part.rar").write_bytes(b"")
assert detect_media_type(_parsed("movie"), tmp_path) == "other"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "other"
def test_folder_mixed_returns_unknown(self, tmp_path: Path):
(tmp_path / "main.mkv").write_bytes(b"")
(tmp_path / "extras.iso").write_bytes(b"")
assert detect_media_type(_parsed("movie"), tmp_path) == "unknown"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "unknown"
def test_empty_folder_keeps_parsed_type(self, tmp_path: Path):
assert detect_media_type(_parsed("tv_show"), tmp_path) == "tv_show"
assert detect_media_type(_parsed("tv_show"), tmp_path, _KB) == "tv_show"
def test_folder_only_metadata_keeps_parsed_type(self, tmp_path: Path):
(tmp_path / "info.nfo").write_bytes(b"")
(tmp_path / "cover.jpg").write_bytes(b"")
(tmp_path / "subs.srt").write_bytes(b"")
# All metadata → conclusive set empty → falls through.
assert detect_media_type(_parsed("movie"), tmp_path) == "movie"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "movie"
# --------------------------------------------------------------------------- #
@@ -109,18 +112,18 @@ class TestMetadataIgnored:
(tmp_path / "info.nfo").write_bytes(b"")
(tmp_path / "cover.jpg").write_bytes(b"")
(tmp_path / "subs.srt").write_bytes(b"")
assert detect_media_type(_parsed("movie"), tmp_path) == "movie"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "movie"
def test_non_video_plus_metadata_still_other(self, tmp_path: Path):
(tmp_path / "disc.iso").write_bytes(b"")
(tmp_path / "info.nfo").write_bytes(b"")
assert detect_media_type(_parsed("movie"), tmp_path) == "other"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "other"
def test_case_insensitive_extensions(self, tmp_path: Path):
# Suffix is lowercased before classification.
f = tmp_path / "X.MKV"
f.write_bytes(b"")
assert detect_media_type(_parsed("movie"), f) == "movie"
assert detect_media_type(_parsed("movie"), f, _KB) == "movie"
# --------------------------------------------------------------------------- #
@@ -132,11 +135,11 @@ class TestMissing:
def test_nonexistent_path_keeps_parsed_type(self, tmp_path: Path):
missing = tmp_path / "does_not_exist.mkv"
# Doesn't exist → empty extension set → falls through.
assert detect_media_type(_parsed("movie"), missing) == "movie"
assert detect_media_type(_parsed("movie"), missing, _KB) == "movie"
def test_nonexistent_folder_keeps_parsed_type(self, tmp_path: Path):
missing = tmp_path / "ghost"
assert detect_media_type(_parsed("tv_show"), missing) == "tv_show"
assert detect_media_type(_parsed("tv_show"), missing, _KB) == "tv_show"
def test_subfolder_not_recursed(self, tmp_path: Path):
# _collect_extensions scans only the first level — files inside
@@ -145,4 +148,4 @@ class TestMissing:
sub.mkdir()
(sub / "deep.mkv").write_bytes(b"")
# Top level has no files at all → empty → falls through to parsed type.
assert detect_media_type(_parsed("movie"), tmp_path) == "movie"
assert detect_media_type(_parsed("movie"), tmp_path, _KB) == "movie"
@@ -37,6 +37,7 @@ def _bare(**overrides) -> ParsedRelease:
raw="X",
normalised="X",
title="X",
title_sanitized="X",
year=None,
season=None,
episode=None,
+6 -15
View File
@@ -9,7 +9,6 @@ Four use cases compute library paths from a release name + TMDB metadata:
Coverage:
- ``TestSanitize`` — Windows-forbidden chars stripped.
- ``TestFindExistingTvshowFolders`` — empty root, prefix match (case + space → dot).
- ``TestResolveSeriesFolderInternal`` — confirmed_folder, no existing, single match,
ambiguous → _Clarification.
@@ -32,7 +31,6 @@ from alfred.application.filesystem.resolve_destination import (
_Clarification,
_find_existing_tvshow_folders,
_resolve_series_folder,
_sanitize,
resolve_episode_destination,
resolve_movie_destination,
resolve_season_destination,
@@ -51,15 +49,6 @@ REL_SERIES = "Oz.Complete.Series.1080p.WEBRip.x265-KONTRAST"
# --------------------------------------------------------------------------- #
class TestSanitize:
def test_passthrough_safe_chars(self):
assert _sanitize("Oz.1997.1080p-GRP") == "Oz.1997.1080p-GRP"
def test_strips_windows_forbidden(self):
# ? : * " < > | \
assert _sanitize('a?b:c*d"e<f>g|h\\i') == "abcdefghi"
# --------------------------------------------------------------------------- #
# _find_existing_tvshow_folders #
# --------------------------------------------------------------------------- #
@@ -107,6 +96,7 @@ class TestResolveSeriesFolderInternal:
out = _resolve_series_folder(
tmp_path,
"Oz",
"Oz",
1997,
"Oz.1997.WEBRip-KONTRAST",
confirmed_folder="Oz.1997.X-GRP",
@@ -117,6 +107,7 @@ class TestResolveSeriesFolderInternal:
out = _resolve_series_folder(
tmp_path,
"Oz",
"Oz",
1997,
"Oz.1997.WEBRip-KONTRAST",
confirmed_folder="Oz.1997.New-X",
@@ -125,21 +116,21 @@ class TestResolveSeriesFolderInternal:
def test_no_existing_returns_computed_as_new(self, tmp_path):
out = _resolve_series_folder(
tmp_path, "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
tmp_path, "Oz", "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
)
assert out == ("Oz.1997.WEBRip-KONTRAST", True)
def test_single_existing_matching_computed_returns_existing(self, tmp_path):
(tmp_path / "Oz.1997.WEBRip-KONTRAST").mkdir()
out = _resolve_series_folder(
tmp_path, "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
tmp_path, "Oz", "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
)
assert out == ("Oz.1997.WEBRip-KONTRAST", False)
def test_single_existing_different_name_returns_clarification(self, tmp_path):
(tmp_path / "Oz.1997.BluRay-OTHER").mkdir()
out = _resolve_series_folder(
tmp_path, "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
tmp_path, "Oz", "Oz", 1997, "Oz.1997.WEBRip-KONTRAST", None
)
assert isinstance(out, _Clarification)
assert "Oz" in out.question
@@ -149,7 +140,7 @@ class TestResolveSeriesFolderInternal:
def test_multiple_existing_returns_clarification(self, tmp_path):
(tmp_path / "Oz.1997.A-GRP").mkdir()
(tmp_path / "Oz.1997.B-GRP").mkdir()
out = _resolve_series_folder(tmp_path, "Oz", 1997, "Oz.1997.A-GRP", None)
out = _resolve_series_folder(tmp_path, "Oz", "Oz", 1997, "Oz.1997.A-GRP", None)
assert isinstance(out, _Clarification)
# Computed already in existing → not duplicated.
assert out.options.count("Oz.1997.A-GRP") == 1