"""Log-Ziel: der Dienst loggt nach stdout, nicht nach stderr. README und docs/INSTALLATION.md versprechen stdout. `logging.basicConfig()` ohne `stream=` nimmt aber stderr. Für journald ist das egal, für den in der Doku beschriebenen Vordergrund-Notbehelf und für jede Weiterleitung der Ausgabe nicht. Gleichzeitig muss die Trennung in `--check-config` bleiben: Infos und Warnungen nach stdout, Fehler nach stderr. """ from __future__ import annotations import io import logging import sys from contextlib import contextmanager from pathlib import Path from unittest.mock import patch from pdf_ocr_hotfolder.__main__ import _setup_logging, main @contextmanager def _fresh_root_logger(): """Root-Logger wie beim echten Dienststart: ohne Handler. `logging.basicConfig()` tut nichts, solange der Root-Logger Handler hat — und pytest hängt seinen Capture-Handler dort ein, nachdem die Fixtures gelaufen sind. Deshalb erst hier, direkt um den Aufruf herum, leeren. """ root = logging.getLogger() saved_handlers, saved_level = root.handlers[:], root.level root.handlers = [] try: yield root finally: for h in root.handlers: h.close() root.handlers = saved_handlers root.level = saved_level def test_setup_logging_uses_stdout() -> None: with _fresh_root_logger() as root: _setup_logging("INFO") streams = [h.stream for h in root.handlers if isinstance(h, logging.StreamHandler)] assert streams, "kein StreamHandler konfiguriert" assert all(s is sys.stdout for s in streams) assert not any(s is sys.stderr for s in streams) def test_log_records_land_on_stdout(monkeypatch) -> None: """Ein echter Log-Satz muss im stdout-Puffer stehen, nicht im stderr.""" out, err = io.StringIO(), io.StringIO() monkeypatch.setattr(sys, "stdout", out) monkeypatch.setattr(sys, "stderr", err) with _fresh_root_logger(): _setup_logging("INFO") logging.getLogger("pdf_ocr_hotfolder.test").warning("Testmeldung 4711") assert "Testmeldung 4711" in out.getvalue() assert "Testmeldung 4711" not in err.getvalue() def test_setup_logging_respects_level() -> None: with _fresh_root_logger() as root: _setup_logging("WARNING") assert root.level == logging.WARNING def test_setup_logging_falls_back_on_garbage_level() -> None: """Ein Tippfehler in [logging].level darf den Start nicht verhindern.""" with _fresh_root_logger() as root: _setup_logging("LAUT") assert root.level == logging.INFO # ---------------- Trennung in --check-config ---------------- def _cfg(tmp_path: Path) -> Path: cfg = tmp_path / "cfg.toml" cfg.write_text(f""" [paths] incoming = "{tmp_path / 'in'}" outgoing = "{tmp_path / 'out'}" working = "{tmp_path / 'work'}" error = "{tmp_path / 'err'}" """) return cfg def test_check_config_keeps_info_on_stdout(tmp_path: Path, monkeypatch, capsys) -> None: monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(_cfg(tmp_path)), "--check-config"]) with patch("pdf_ocr_hotfolder.service.shutil.which", return_value="/usr/bin/fake"): assert main() == 0 captured = capsys.readouterr() assert "Config sauber" in captured.out assert captured.err == "" def test_check_config_keeps_errors_on_stderr(tmp_path: Path, monkeypatch, capsys) -> None: cfg = tmp_path / "cfg.toml" cfg.write_text('[ocr]\nlanguages = "deu"\n') monkeypatch.setattr(sys, "argv", ["pdf-ocr-hotfolder", "--config", str(cfg), "--check-config"]) assert main() == 2 captured = capsys.readouterr() assert "FEHLER" in captured.err assert "FEHLER" not in captured.out