epub2md/tests/test_integration.py

126 lines
5 KiB
Python
Raw Normal View History

import json
import re
from pathlib import Path
import pytest
import yaml
from conftest import ROOT, Book, Doc, build_epub
BEISPIELE = sorted((ROOT / "Beispiele").glob("*.epub"))
def simple_book() -> Book:
return Book(docs=[Doc("a.xhtml", "<h1>A</h1><p>Text</p>"), Doc("b.xhtml", "<h1>B</h1><p>Mehr</p>")])
def test_output_structure_and_reports(convert):
run = convert(simple_book(), "--copy-original")
assert run.rc == 0
for rel in ("README.md", "metadata.yaml", "manifest.json", "conversion-report.json",
"diagnostics/warnings.md", "diagnostics/unsupported-elements.md"):
assert (run.out / rel).is_file(), rel
assert len(list((run.out / "source").glob("*.epub"))) == 1
manifest = json.loads((run.out / "manifest.json").read_text(encoding="utf-8"))
assert [c["file"] for c in manifest["chapters"]] == ["chapters/001-a.md", "chapters/002-b.md"]
for c in manifest["chapters"]:
assert (run.out / c["file"]).is_file()
assert c["word_count"] > 0
assert run.report["status"] == "success"
assert run.report["input"]["sha256"]
fm = yaml.safe_load(run.chapter("001").split("---\n")[1])
assert fm["chapter_number"] == 1 and fm["source_href"] == "a.xhtml" and fm["book_title"] == "Testbuch"
def test_no_frontmatter_no_toc(convert):
run = convert(simple_book(), "--no-frontmatter", "--no-toc")
assert not run.chapter("001").startswith("---")
assert not (run.out / "README.md").exists()
def test_dry_run_writes_nothing(convert, tmp_path):
report = tmp_path / "r.json"
run = convert(simple_book(), "--dry-run", "--report", str(report))
assert run.rc == 0
assert not run.out.exists()
assert not report.exists()
assert "chapters/001-a.md" in run.stdout
def test_existing_output_requires_force(convert, tmp_path):
out = tmp_path / "ziel"
out.mkdir()
(out / "wichtig.txt").write_text("nicht löschen")
run = convert(simple_book(), out=out)
assert run.rc == 1
assert (out / "wichtig.txt").exists()
# --force refuses directories that are not epub2md output
assert convert(simple_book(), "--force", out=out).rc == 1
assert (out / "wichtig.txt").exists()
def test_force_replaces_previous_output(convert, tmp_path):
out = tmp_path / "ziel"
assert convert(simple_book(), out=out).rc == 0
(out / "alt.txt").write_text("alt")
run = convert(simple_book(), "--force", out=out)
assert run.rc == 0
assert not (out / "alt.txt").exists()
assert (out / "README.md").exists()
assert not list(tmp_path.glob(".epub2md-*")) # temp dir cleaned up
def test_report_option_and_verbose(convert, tmp_path):
report = tmp_path / "rep" / "bericht.json"
run = convert(simple_book(), "--report", str(report), "--verbose")
assert json.loads(report.read_text(encoding="utf-8"))["tool"] == "epub2md"
assert "Rendere" in run.stderr
def test_invalid_arguments_exit_1(convert, tmp_path):
assert convert(simple_book(), "--math", "falsch").rc == 1
assert convert(simple_book(), "--chapter-level", "9").rc == 1
assert convert(tmp_path / "gibt-es-nicht.epub").rc == 1
def test_deterministic_output(convert, tmp_path):
epub = build_epub(tmp_path / "same.epub", simple_book())
a = convert(epub, out=tmp_path / "x1")
b = convert(epub, out=tmp_path / "x2")
for p in a.chapters():
assert p.read_text() == (b.out / "chapters" / p.name).read_text()
def test_encoding_edge_cases(convert):
text_in = "Ärger über Öl – „Zitat“ — café naïve, Ωμέγα αβγ, ∀x∈ℝ: x² ≥ 0 … ‚einfach‘"
book = Book(docs=[Doc("Kapitel-é.xhtml", f'<h1 id="über">Grüße</h1><p>{text_in}</p>')])
run = convert(book)
text = run.chapter("001")
assert text_in in text
assert "<EFBFBD>" not in text
assert run.chapters()[0].name == "001-gruesse.md"
@pytest.mark.skipif(not BEISPIELE, reason="keine Beispiel-EPUBs vorhanden")
@pytest.mark.parametrize("epub", BEISPIELE, ids=[p.stem[:20] for p in BEISPIELE])
def test_example_books_structural_invariants(convert, epub: Path):
"""Checks structure only – no copyrighted text is asserted."""
run = convert(epub)
assert run.rc == 0, run.stderr
report = run.report
chapters = run.chapters()
assert len(chapters) >= 3
assert report["output_link_check"]["broken_count"] == 0, report["output_link_check"]["broken"][:5]
assert not report["errors"]
for path in chapters:
text = path.read_text(encoding="utf-8")
body = text.split("---\n", 2)[2] if text.startswith("---\n") else text
yaml.safe_load(text.split("---\n")[1])
assert body.strip(), f"leere Kapiteldatei {path.name}"
fences = re.findall(r"^(`{3,})", body, re.M)
assert len(fences) % 2 == 0, f"unbalancierte Code-Fences in {path.name}"
for block in re.findall(r"^```[\w+-]*\n(.*?)\n```$", body, re.M | re.S):
assert "&lt;" not in block and "&amp;" not in block
for m in report["media"]["files"]:
assert (run.out / m["file"]).is_file()
assert sum(c["stats"]["word_count"] for c in report["chapters"]) > 1000