wip: estado de trabajo pendiente antes de la vista grid (suite 230 verde)
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
"""Tests for per-language subtitle selection and dict-typed ``Config.languages``.
|
||||
|
||||
These tests are pure: no network, no yt-dlp, no DB. They cover the policy
|
||||
logic that decides which subtitle track to pick for a given
|
||||
``info`` dict produced by yt-dlp, plus the legacy/back-compat shims that
|
||||
let older YAML configs still load.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from yt_scraper.config import Config, parse_languages
|
||||
from yt_scraper.extract import pick_subtitle
|
||||
|
||||
|
||||
# ---------- fixtures ----------
|
||||
|
||||
def _track(ext: str = "json3", url: str = "u") -> dict:
|
||||
return {"ext": ext, "url": url}
|
||||
|
||||
|
||||
def _info(manual: dict | None = None, auto: dict | None = None) -> dict:
|
||||
return {"subtitles": manual or {}, "automatic_captions": auto or {}}
|
||||
|
||||
|
||||
# ---------- pick_subtitle: mixed per-language mode ----------
|
||||
|
||||
def test_mixed_manual_and_auto_per_language() -> None:
|
||||
"""`es: auto` only takes auto; iterating `es` first means it wins.
|
||||
|
||||
With ``{"es": "auto", "en": "manual"}`` the iteration matches `es`
|
||||
against the auto dict and picks the auto track; for `en` only the
|
||||
manual dict is consulted and the manual track is returned. The two
|
||||
policies do NOT cross-pollinate between languages.
|
||||
"""
|
||||
info = _info(
|
||||
manual={"en": [_track(url="man-en")]},
|
||||
auto={"en": [_track(url="auto-en")], "es": [_track(url="auto-es")]},
|
||||
)
|
||||
pick = pick_subtitle(info, {"es": "auto", "en": "manual"}, prefer_manual=True)
|
||||
assert pick is not None
|
||||
assert pick.lang == "es"
|
||||
assert pick.source == "auto"
|
||||
|
||||
|
||||
def test_manual_only_skips_auto_even_when_present() -> None:
|
||||
info = _info(manual={}, auto={"es": [_track(url="auto")]})
|
||||
pick = pick_subtitle(info, {"es": "manual"}, prefer_manual=True)
|
||||
assert pick is None
|
||||
|
||||
|
||||
def test_auto_only_skips_manual_even_when_present() -> None:
|
||||
info = _info(manual={"es": [_track(url="man")]}, auto={})
|
||||
pick = pick_subtitle(info, {"es": "auto"}, prefer_manual=True)
|
||||
assert pick is None
|
||||
|
||||
|
||||
def test_any_defer_to_prefer_manual_default_true() -> None:
|
||||
"""`any` honours the legacy prefer_manual=True default."""
|
||||
info = _info(
|
||||
manual={"es": [_track(url="man")]},
|
||||
auto={"es": [_track(url="auto")]},
|
||||
)
|
||||
pick = pick_subtitle(info, {"es": "any"}, prefer_manual=True)
|
||||
assert pick is not None
|
||||
assert pick.source == "manual"
|
||||
|
||||
|
||||
def test_any_defer_to_prefer_manual_default_false() -> None:
|
||||
info = _info(
|
||||
manual={"es": [_track(url="man")]},
|
||||
auto={"es": [_track(url="auto")]},
|
||||
)
|
||||
pick = pick_subtitle(info, {"es": "any"}, prefer_manual=False)
|
||||
assert pick is not None
|
||||
assert pick.source == "auto"
|
||||
|
||||
|
||||
def test_picks_best_format_within_track() -> None:
|
||||
info = _info(manual={"en": [_track(ext="ttml", url="t"), _track(ext="json3", url="j")]})
|
||||
pick = pick_subtitle(info, {"en": "manual"}, prefer_manual=True)
|
||||
assert pick.url == "j"
|
||||
|
||||
|
||||
# ---------- language normalisation ----------
|
||||
|
||||
def test_language_base_match() -> None:
|
||||
"""`es-419` preference matches the `es` caption track."""
|
||||
info = _info(manual={"es": [_track(url="u")]})
|
||||
pick = pick_subtitle(info, {"es-419": "manual"}, prefer_manual=True)
|
||||
assert pick is not None
|
||||
assert pick.lang == "es"
|
||||
|
||||
|
||||
# ---------- legacy list input ----------
|
||||
|
||||
def test_legacy_list_input_uses_prefer_manual() -> None:
|
||||
info = _info(
|
||||
manual={"es": [_track(url="m")]},
|
||||
auto={"es": [_track(url="a")]},
|
||||
)
|
||||
pick = pick_subtitle(info, ["es"], prefer_manual=True)
|
||||
assert pick.source == "manual"
|
||||
pick = pick_subtitle(info, ["es"], prefer_manual=False)
|
||||
assert pick.source == "auto"
|
||||
|
||||
|
||||
def test_unknown_mode_falls_back_to_any() -> None:
|
||||
info = _info(manual={"es": [_track()]}, auto={"es": [_track()]})
|
||||
pick = pick_subtitle(info, {"es": "garbage"}, prefer_manual=True)
|
||||
assert pick is not None
|
||||
assert pick.source == "manual"
|
||||
|
||||
|
||||
# ---------- parse_languages config helper ----------
|
||||
|
||||
def test_parse_languages_dict_pass_through() -> None:
|
||||
assert parse_languages({"en": "manual", "es": "auto"}, True) == {
|
||||
"en": "manual", "es": "auto",
|
||||
}
|
||||
|
||||
|
||||
def test_parse_languages_dict_unknown_mode_to_any() -> None:
|
||||
assert parse_languages({"en": "garbage"}, True) == {"en": "any"}
|
||||
|
||||
|
||||
def test_parse_languages_legacy_list_to_manual_by_default() -> None:
|
||||
assert parse_languages(["es", "en"], prefer_manual=True) == {
|
||||
"es": "manual", "en": "manual",
|
||||
}
|
||||
|
||||
|
||||
def test_parse_languages_legacy_list_to_auto_when_prefer_manual_false() -> None:
|
||||
assert parse_languages(["es", "en"], prefer_manual=False) == {
|
||||
"es": "auto", "en": "auto",
|
||||
}
|
||||
|
||||
|
||||
def test_parse_languages_none_returns_empty() -> None:
|
||||
assert parse_languages(None, True) == {}
|
||||
|
||||
|
||||
# ---------- Config dataclass default shape ----------
|
||||
|
||||
def test_config_languages_defaults_to_dict() -> None:
|
||||
cfg = Config()
|
||||
assert isinstance(cfg.languages, dict)
|
||||
# "any", not "manual": the default must not exclude auto-generated captions.
|
||||
assert cfg.languages == {"es": "any", "en": "any"}
|
||||
|
||||
|
||||
def test_config_prefer_manual_defaults_true() -> None:
|
||||
cfg = Config()
|
||||
assert cfg.prefer_manual is True
|
||||
Reference in New Issue
Block a user