"""Tests for yt_untertitel.py; yt-dlp is always replaced by fakes (no network).""" 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": []}, } pytestmark = pytest.mark.usefixtures("yt_dlp_installed") 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 @pytest.mark.parametrize("info, expected", [ (AUTO_ONLY, "de"), ({"language": "fr", "automatic_captions": {}}, "fr"), ({"subtitles": {"en": []}}, None), ]) def test_original_language(info, expected): assert yt.original_language(info) == 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(fake_yt_dlp, 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_yt_dlp_runs_in_temporary_directory(fake_yt_dlp, tmp_path, monkeypatch): # ffmpeg -bsfs, called by yt-dlp, leaves a file "3" in its working directory run = fake_yt_dlp(AUTO_ONLY) monkeypatch.setattr(yt.subprocess, "run", run) monkeypatch.chdir(tmp_path) run_main(monkeypatch, "-u", URL, "-n", "Rede") assert all(cwd is not None and Path(cwd) != tmp_path for cwd in run.cwds) template = run.calls[1][run.calls[1].index("-o") + 1] assert template == f"{tmp_path / 'Rede'}.%(ext)s" assert (tmp_path / "Rede.de-orig.vtt").is_file() def test_main_prefers_manual_and_defaults_to_video_id(fake_yt_dlp, 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(fake_yt_dlp, 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, cwd=None): 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(fake_yt_dlp, 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