"""Ende-zu-Ende: Kommandozeilenaufruf auf den Test-PDFs.""" import difflib import re import subprocess import sys import zipfile import pytest from conftest import ROOT, TITLE, needs_pandoc, needs_tesseract from pdf2md import split_frontmatter SCRIPT = ROOT / "pdf2md.py" def run(*args, cwd=None) -> subprocess.CompletedProcess: done = subprocess.run( [sys.executable, str(SCRIPT), *map(str, args)], capture_output=True, text=True, timeout=600, cwd=cwd, ) assert done.returncode == 0, done.stderr return done def words(text: str) -> list[str]: return re.findall(r"[^\W\d_]+", text.lower()) @pytest.fixture(scope="module") def digital_md(pdfs, tmp_path_factory): out = tmp_path_factory.mktemp("digital") / "digital.md" run(pdfs["digital"], out) return out.read_text(encoding="utf-8") def test_digital_structure(digital_md): headings = re.findall(r"^#+ .*$", digital_md, re.MULTILINE) assert headings == [ "# Das Testbuch", "## Inhaltsverzeichnis", "## Teil I: Der Anfang", "### 1 Der Notar", "### 2 Ein Brief", ] # Absatz über die Seitengrenze mit Silbentrennung assert "Dort sprach er lange mit dem Geheimdienst über die Lage" in digital_md # keine Seitenzahlen, keine Kolumnentitel außer der Titelüberschrift assert not re.search(r"^\d+$", digital_md, re.MULTILINE) meta, body = split_frontmatter(digital_md) assert meta["title"] == TITLE and meta["lang"] == "de" assert body.count(TITLE) == 1 @needs_tesseract def test_scanned_matches_digital(pdfs, tmp_path, digital_md): out = tmp_path / "scanned.md" done = run(pdfs["scanned"], out, "--jobs", "2") assert "4 Seiten werden per OCR gelesen" in done.stdout md = out.read_text(encoding="utf-8") ratio = difflib.SequenceMatcher(None, words(digital_md), words(md), autojunk=False).ratio() assert ratio >= 0.9, ratio assert "Geheimdienst über die Lage" in md assert re.search(r"^#+ 1 Der Notar$", md, re.MULTILINE) assert re.search(r"^#+ 2 Ein Brief$", md, re.MULTILINE) assert not re.search(r"^\d+$", md, re.MULTILINE) @needs_tesseract def test_existing_ocr_layer_is_reused(pdfs, tmp_path, digital_md): out = tmp_path / "scanned_ocr.md" done = run(pdfs["scanned_ocr"], out) assert "4 mit OCR-Ebene" in done.stdout assert "0 Seiten werden per OCR gelesen" in done.stdout md = out.read_text(encoding="utf-8") ratio = difflib.SequenceMatcher(None, words(digital_md), words(md), autojunk=False).ratio() assert ratio >= 0.9, ratio @needs_tesseract def test_mixed_keeps_page_order(pdfs, tmp_path): out = tmp_path / "mixed.md" done = run(pdfs["mixed"], out) assert "2 Seiten werden per OCR gelesen" in done.stdout md = out.read_text(encoding="utf-8") positions = [md.find(s) for s in ("Albrecht van Oie", "Geheimdienst", "Teurer Albrecht", "nächsten Morgen")] assert -1 not in positions assert positions == sorted(positions) @needs_tesseract def test_photo_page_produces_no_garbage(pdfs, tmp_path, digital_md): out = tmp_path / "photo.md" done = run(pdfs["photo"], out) assert "0 Seiten werden per OCR gelesen" in done.stdout assert "Hinweis: 1 Bildseite" in done.stdout md = out.read_text(encoding="utf-8") assert words(md)[-5:] == words(digital_md)[-5:] def test_no_ocr_on_scan_yields_hint(pdfs, tmp_path): out = tmp_path / "noocr.md" done = run(pdfs["scanned"], out, "--no-ocr") assert "Hinweis: 4 Bildseite(n)" in done.stdout assert "Albrecht" not in out.read_text(encoding="utf-8") @needs_pandoc def test_epub(pdfs, tmp_path): out = tmp_path / "buch.md" run(pdfs["digital"], out, "--epub") epub = tmp_path / "buch.epub" images = tmp_path / "buch_images" assert (images / "cover.jpg").is_file() assert {f.suffix for f in images.iterdir()} == {".jpg"} with zipfile.ZipFile(epub) as z: assert z.read("mimetype") == b"application/epub+zip" nav = z.read("EPUB/nav.xhtml").decode() opf = z.read("EPUB/content.opf").decode() for title in ("Teil I: Der Anfang", "1 Der Notar", "2 Ein Brief"): assert title in nav assert "Erika Muster" in opf assert "cover" in opf def test_png_on_request(pdfs, tmp_path): out = tmp_path / "png.md" run(pdfs["digital"], out, "--images", "--image-format", "png") md = out.read_text(encoding="utf-8") assert re.search(r"!\[\]\(png_images/[^)]+\.png\)", md) def test_image_links_relative_to_markdown_with_relative_output(pdfs, tmp_path): # Aufruf wie "python3 pdf2md.py --images buch.pdf Output/buch.md" aus dem Projektordner run(pdfs["digital"], "Output/buch.md", "--images", cwd=tmp_path) md = (tmp_path / "Output" / "buch.md").read_text(encoding="utf-8") links = re.findall(r"!\[\]\(([^)]+)\)", md) assert links for link in links: assert link.startswith("buch_images/"), link assert (tmp_path / "Output" / link).is_file()