"""Tests für `--check-config`. Die Exit-Codes werden vom Updater ausgewertet und müssen verlässlich sein: 0 = sauber, 1 = nur Warnungen, 2 = Fehler. """ from __future__ import annotations import sys from pathlib import Path from unittest.mock import patch import pytest from pdf_ocr_hotfolder.__main__ import CHECK_ERROR, CHECK_OK, CHECK_WARN, main def _cfg_file(tmp_path: Path, tmp_config, extra: str = "") -> Path: cfg_file = tmp_path / "cfg.toml" cfg_file.write_text(f""" [paths] incoming = "{tmp_config.paths.incoming}" outgoing = "{tmp_config.paths.outgoing}" working = "{tmp_config.paths.working}" error = "{tmp_config.paths.error}" """ + extra) return cfg_file def _check(monkeypatch, cfg_file: Path, binaries_present: bool = True) -> int: monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(cfg_file), "--check-config"]) which = "/usr/bin/fake" if binaries_present else None with patch("pdf_ocr_hotfolder.service.shutil.which", return_value=which): return main() # ---------------- Exit 0 ---------------- def test_clean_config_returns_0(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config)) assert rc == CHECK_OK out = capsys.readouterr().out assert "Config sauber" in out def test_check_does_not_process_files(tmp_path, tmp_config, monkeypatch) -> None: """Der Check darf nichts verarbeiten und nichts verschieben.""" pdf = tmp_config.paths.incoming / "scan.pdf" pdf.write_bytes(b"%PDF-1.4\n") assert _check(monkeypatch, _cfg_file(tmp_path, tmp_config)) == CHECK_OK assert pdf.exists() assert list(tmp_config.paths.outgoing.iterdir()) == [] # ---------------- Exit 1 (nur Warnungen) ---------------- def test_legacy_timeout_returns_1(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config, "\n[ocr]\ntimeout = 1800\n")) assert rc == CHECK_WARN out = capsys.readouterr().out assert "WARNUNG" in out assert "PRO SEITE" in out def test_unknown_key_returns_1(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config, '\n[ocr]\nlangauges = "deu"\n')) assert rc == CHECK_WARN assert "[ocr].langauges" in capsys.readouterr().out def test_pdfa_level_with_healthy_ghostscript_returns_1( tmp_path, tmp_config, monkeypatch, capsys) -> None: """Gesundes Ghostscript: nur Hinweis (Exit 1), kein Abbruch.""" monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(_cfg_file(tmp_path, tmp_config, '\n[ocr]\npdfa_level = "2"\n')), "--check-config"]) with patch("pdf_ocr_hotfolder.service.shutil.which", return_value="/usr/bin/fake"), \ patch("pdf_ocr_hotfolder.service.detect_ghostscript_version", return_value="10.02.1"): rc = main() assert rc == CHECK_WARN assert "Ghostscript" in capsys.readouterr().out # ---------------- Exit 2 (Fehler) ---------------- def test_missing_config_file_returns_2(tmp_path, monkeypatch, capsys) -> None: monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(tmp_path / "gibtsnicht.toml"), "--check-config"]) assert main() == CHECK_ERROR assert "nicht gefunden" in capsys.readouterr().err def test_broken_paths_section_returns_2(tmp_path, monkeypatch, capsys) -> None: cfg_file = tmp_path / "cfg.toml" cfg_file.write_text('[ocr]\nlanguages = "deu"\n') assert _check(monkeypatch, cfg_file) == CHECK_ERROR assert "[paths]" in capsys.readouterr().err def test_invalid_toml_returns_2(tmp_path, monkeypatch, capsys) -> None: """Kaputtes TOML: saubere Meldung statt Traceback.""" cfg_file = tmp_path / "cfg.toml" cfg_file.write_text("[paths\nincoming = ") assert _check(monkeypatch, cfg_file) == CHECK_ERROR assert "TOML" in capsys.readouterr().err def test_missing_binaries_return_2(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config), binaries_present=False) assert rc == CHECK_ERROR err = capsys.readouterr().err assert "tesseract" in err def test_invalid_name_mode_returns_2(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config, '\n[output]\nname_mode = "praefix"\n')) assert rc == CHECK_ERROR assert "name_mode" in capsys.readouterr().err def test_archive_without_dir_returns_2(tmp_path, tmp_config, monkeypatch, capsys) -> None: rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config, '\n[output]\noriginal_on_success = "archive"\n')) assert rc == CHECK_ERROR assert "archive_dir" in capsys.readouterr().err def test_error_beats_warning(tmp_path, tmp_config, monkeypatch) -> None: """Fehler + Warnung → Exit 2, nicht 1.""" rc = _check(monkeypatch, _cfg_file(tmp_path, tmp_config, '\n[ocr]\ntimeout = 1800\n\n[output]\nname_mode = "x"\n')) assert rc == CHECK_ERROR # ---------------- Zusammenspiel mit anderen Optionen ---------------- def test_check_config_wins_over_once(tmp_path, tmp_config, monkeypatch) -> None: """--check-config hat Vorrang: es wird nichts verarbeitet.""" pdf = tmp_config.paths.incoming / "scan.pdf" pdf.write_bytes(b"%PDF-1.4\n") monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(_cfg_file(tmp_path, tmp_config)), "--once", "--check-config"]) with patch("pdf_ocr_hotfolder.service.shutil.which", return_value="/usr/bin/fake"): assert main() == CHECK_OK assert pdf.exists() @pytest.mark.parametrize("code,expected", [(CHECK_OK, 0), (CHECK_WARN, 1), (CHECK_ERROR, 2)]) def test_exit_code_constants(code: int, expected: int) -> None: """Die Konstanten sind Teil der Schnittstelle zum Updater.""" assert code == expected