"""Minimale Tests für die Kernlogik: Token-Suche, Geometrie, Fehlerfälle.""" import re import fitz import pytest from core import ( PT_TO_MM, PlacementError, build_a4_svg, find_token_occurrences, parse_svg, place_all, signatur_geometrie, ) from models import SignatureJob # ── Test-Hilfen ───────────────────────────────────────────────────────── SVG_OK = ( b'' b'' ) SVG_NO_VIEWBOX = ( b'' b'' ) def make_pdf(tokens_at): """Erzeugt ein A4-PDF (1 Seite) mit den Token an gegebenen Punkt-Positionen.""" doc = fitz.open() page = doc.new_page(width=595.0, height=842.0) # A4 in pt for token, (x, y) in tokens_at: page.insert_text((x, y), token, fontsize=11) pdf_bytes = doc.tobytes() doc.close() return pdf_bytes def _translate_from_svg(a4_svg): """Liest tx, ty aus dem ersten translate(...) des A4-SVG.""" m = re.search(r"translate\(([-\d.]+),([-\d.]+)\)", a4_svg) assert m, "kein translate gefunden" return float(m.group(1)), float(m.group(2)) def _scale_from_svg(a4_svg): m = re.search(r"scale\(([-\d.]+)\)", a4_svg) assert m, "kein scale gefunden" return float(m.group(1)) # ── viewBox / SVG ─────────────────────────────────────────────────────── def test_parse_svg_liest_viewbox(): vb = parse_svg(SVG_OK) assert (vb.minx, vb.miny, vb.vw, vb.vh) == (0.0, 0.0, 100.0, 40.0) assert "' b'' ) vb = parse_svg(svg) rect = fitz.Rect(72.0, 72.0, 120.0, 84.0) job = SignatureJob(token="§§SIG1§§", svg_id="s.svg", position="unter") a4 = build_a4_svg(vb, rect, job) tx, ty = _translate_from_svg(a4) skala = _scale_from_svg(a4) # tx = x_sig - minx*skala assert tx == pytest.approx(72.0 * PT_TO_MM - 10.0 * skala, abs=1e-3) def test_versatz_verschiebt_links_rechts_oben_unten(): vb = parse_svg(SVG_OK) rect = fitz.Rect(72.0, 72.0, 120.0, 84.0) base = SignatureJob(token="§§S§§", svg_id="s.svg", position="genau") moved = SignatureJob(token="§§S§§", svg_id="s.svg", position="genau", versatz_x_mm=5.0, versatz_y_mm=-3.0) a4_base = build_a4_svg(vb, rect, base) a4_moved = build_a4_svg(vb, rect, moved) bx, by = _translate_from_svg(a4_base) mx, my = _translate_from_svg(a4_moved) assert mx == pytest.approx(bx + 5.0, abs=1e-3) # +x → nach rechts assert my == pytest.approx(by - 3.0, abs=1e-3) # −y → nach oben def test_immer_single_stroke_fill_none_und_stroke(): """Signaturen werden immer als Single-Stroke gezeichnet (fill:none + stroke).""" vb = parse_svg(SVG_OK) rect = fitz.Rect(72.0, 72.0, 120.0, 84.0) job = SignatureJob(token="§§S§§", svg_id="s.svg", position="genau", breite_mm=50.0, linienstaerke_mm=0.5) a4 = build_a4_svg(vb, rect, job) assert 'fill="none"' in a4 assert "stroke=" in a4 and "stroke-width=" in a4 # stroke-width in viewBox-Einheiten: 0.5mm / skala (skala = 50/100 = 0.5) = 1.0 import re sw = float(re.search(r'stroke-width="([\d.]+)"', a4).group(1)) assert sw == pytest.approx(1.0, abs=1e-3) def test_root_transform_wird_nicht_uebernommen(): """Ein transform auf der äußersten darf die Platzierung nicht verfälschen.""" svg = ( b'' ) vb = parse_svg(svg) assert "transform" not in vb.group_attrs # nicht mitgeführt assert vb.group_attrs.get("fill") == "blue" # echtes Styling bleibt rect = fitz.Rect(72.0, 72.0, 120.0, 84.0) a4 = build_a4_svg(vb, rect, SignatureJob(token="§§S§§", svg_id="s.svg")) # Genau ein transform (die Positionierung), nicht zwei. assert a4.count("transform=") == 1 # ── Token-Suche ───────────────────────────────────────────────────────── def test_find_token_einmal(): pdf = make_pdf([("§§SIG1§§", (100.0, 200.0))]) doc = fitz.open(stream=pdf, filetype="pdf") occ = find_token_occurrences(doc, "§§SIG1§§") doc.close() assert len(occ) == 1 page_index, rect = occ[0] assert page_index == 0 assert rect.x0 == pytest.approx(100.0, abs=2.0) # nahe Einfügepunkt def test_find_token_nicht_gefunden(): pdf = make_pdf([("§§SIG1§§", (100.0, 200.0))]) doc = fitz.open(stream=pdf, filetype="pdf") assert find_token_occurrences(doc, "§§SIG9§§") == [] doc.close() def test_find_token_mehrfach_erlaubt(): # Derselbe Token mehrfach (z.B. Unterschrift auf mehreren Stellen) → alle Treffer. pdf = make_pdf([("§§SIG1§§", (100.0, 200.0)), ("§§SIG1§§", (100.0, 400.0))]) doc = fitz.open(stream=pdf, filetype="pdf") occ = find_token_occurrences(doc, "§§SIG1§§") doc.close() assert len(occ) == 2 # ── place_all: Gesamtfluss + Fehlerliste ──────────────────────────────── def make_multipage_pdf(per_page_tokens): """Erzeugt ein A4-PDF mit mehreren Seiten; pro Seite eine Liste (token, (x,y)).""" doc = fitz.open() for tokens in per_page_tokens: page = doc.new_page(width=595.0, height=842.0) for token, (x, y) in tokens: page.insert_text((x, y), token, fontsize=11) data = doc.tobytes() doc.close() return data def test_place_all_erfolg(): pdf = make_pdf([("§§SIG1§§", (100.0, 200.0))]) jobs = [SignatureJob(token="§§SIG1§§", svg_id="unterschrift.svg", position="unter")] pages, errors = place_all(pdf, {"unterschrift.svg": SVG_OK}, jobs) assert errors == [] assert len(pages) == 1 p = pages[0] assert p.filename == "seite_1.svg" assert p.tokens == ["§§SIG1§§"] assert p.a4_svg.startswith("