"""Tests for yt_untertitel.py; yt-dlp is always replaced by fakes (no network).""" import json import subprocess import sys from pathlib import Path import pytest import yt_untertitel as yt URL = "https://www.youtube.com/watch?v=abc123" AUTO_ONLY = { "id": "abc123", "title": "Eine Rede", "subtitles": {}, "automatic_captions": {"de-orig": [], "de": [], "en": []}, } def fake_yt_dlp(info, write=True, fail_download=False): """Stand-in for subprocess.run: answers the info request, writes the VTT file.""" calls = [] def run(command, capture_output, text): calls.append(command) if "--dump-single-json" in command: return subprocess.CompletedProcess(command, 0, json.dumps(info), "") if fail_download: return subprocess.CompletedProcess(command, 1, "", "HTTP Error 429") track = command[command.index("--sub-langs") + 1] template = command[command.index("-o") + 1] if write: Path(template.replace("%(ext)s", f"{track}.vtt")).write_text( "WEBVTT\n", encoding="utf-8" ) return subprocess.CompletedProcess(command, 0, "", "") run.calls = calls return run @pytest.fixture(autouse=True) def yt_dlp_installed(monkeypatch): monkeypatch.setattr(yt.shutil, "which", lambda name: f"/usr/bin/{name}") def run_main(monkeypatch, *args): monkeypatch.setattr(sys, "argv", ["yt_untertitel.py", *map(str, args)]) yt.main() # === Track selection === @pytest.mark.parametrize("info, lang, expected", [ ({"subtitles": {"de": []}, "automatic_captions": {"de-orig": []}}, "de", ("de", False)), (AUTO_ONLY, "de", ("de-orig", True)), # "en" exists only as machine translation of the German original (AUTO_ONLY, "en", None), ({"subtitles": None, "automatic_captions": None}, "de", None), ]) def test_choose_track(info, lang, expected): assert yt.choose_track(info, lang) == expected def test_available_tracks(): info = {"subtitles": {"fr": []}, "automatic_captions": {"de-orig": [], "de": []}} assert yt.available_tracks(info) == (["fr"], ["de"]) # === Command line === def test_main_downloads_auto_original(tmp_path, monkeypatch, capsys): run = fake_yt_dlp(AUTO_ONLY) monkeypatch.setattr(yt.subprocess, "run", run) run_main(monkeypatch, "-u", URL, "-n", "Rede", "-d", tmp_path / "neu") assert (tmp_path / "neu" / "Rede.de-orig.vtt").is_file() download = run.calls[1] assert "--write-auto-subs" in download and "--write-subs" not in download assert download[-1] == URL out = capsys.readouterr().out assert "automatische Untertitel „de-orig“" in out assert "vtt2txt.py -i" in out def test_main_prefers_manual_and_defaults_to_video_id(tmp_path, monkeypatch): info = dict(AUTO_ONLY, subtitles={"de": []}) run = fake_yt_dlp(info) monkeypatch.setattr(yt.subprocess, "run", run) run_main(monkeypatch, "-u", URL, "-d", tmp_path) assert (tmp_path / "abc123.de.vtt").is_file() assert "--write-subs" in run.calls[1] def test_main_no_matching_track(tmp_path, monkeypatch): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(AUTO_ONLY)) with pytest.raises(SystemExit, match="Automatisch \\(Originalsprache\\): de"): run_main(monkeypatch, "-u", URL, "-l", "en", "-d", tmp_path) def test_main_info_error(tmp_path, monkeypatch): def run(command, capture_output, text): return subprocess.CompletedProcess(command, 1, "", "ERROR: Video unavailable") monkeypatch.setattr(yt.subprocess, "run", run) with pytest.raises(SystemExit, match="Video unavailable"): run_main(monkeypatch, "-u", URL, "-d", tmp_path) @pytest.mark.parametrize("kwargs, message", [ ({"fail_download": True}, "HTTP Error 429"), ({"write": False}, "keine Datei"), ]) def test_main_download_errors(tmp_path, monkeypatch, kwargs, message): monkeypatch.setattr(yt.subprocess, "run", fake_yt_dlp(AUTO_ONLY, **kwargs)) with pytest.raises(SystemExit, match=message): run_main(monkeypatch, "-u", URL, "-d", tmp_path) @pytest.mark.parametrize("args, message", [ ([], "required"), (["-u", URL, "-n", "sub/Rede"], "ohne Verzeichnis"), (["-u", URL, "-n", ""], "ohne Verzeichnis"), ]) def test_cli_errors(monkeypatch, capsys, args, message): with pytest.raises(SystemExit) as exc: run_main(monkeypatch, *args) assert exc.value.code == 2 assert message in capsys.readouterr().err def test_cli_without_yt_dlp(monkeypatch, capsys): monkeypatch.setattr(yt.shutil, "which", lambda name: None) with pytest.raises(SystemExit): run_main(monkeypatch, "-u", URL) assert "pip install yt-dlp" in capsys.readouterr().err