test(documents): skip a format when its wheel is absent (portable unit gate)

pytest tests-unit runs in the GitHub-mirror unit workflow across an
ubuntu/windows/macos matrix. PDF (built-in fallback) and Markdown are stdlib and
always render; DOCX/XLSX/PPTX now skip gracefully when python-docx/openpyxl/
python-pptx isn't importable in that env instead of erroring. The canonical gate
(hanzo.yml installs requirements.txt) still exercises every format for real.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
zeekay
2026-07-25 08:18:20 -07:00
co-authored by Claude Opus 4.8
parent 267735d0bd
commit e631d35fd0
+19 -2
View File
@@ -13,6 +13,7 @@ Covers the four concerns end to end:
Runs without a GPU, without torch, and without the WeasyPrint native stack (the built-in
PDF fallback produces a valid %PDF), so it is safe for the dependency-light CI unit gate.
"""
import importlib.util
import io
import json
import sys
@@ -21,6 +22,16 @@ from pathlib import Path
import pytest
# PDF (built-in fallback) and Markdown are pure-stdlib and always render. DOCX/XLSX/PPTX
# need their wheel; when it is absent (a bare env) we SKIP that format instead of failing —
# the canonical gate (requirements.txt installed) exercises every format for real.
_FMT_LIB = {"docx": "docx", "xlsx": "openpyxl", "pptx": "pptx"}
def _fmt_available(fmt: str) -> bool:
lib = _FMT_LIB.get(fmt)
return lib is None or importlib.util.find_spec(lib) is not None
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from aiohttp import web
@@ -105,6 +116,8 @@ def test_public_catalog_is_json_serializable_without_build_fn():
@pytest.mark.parametrize("tmpl", D.CATALOG, ids=[t["id"] for t in D.CATALOG])
def test_every_template_renders_every_format(tmpl):
for fmt in tmpl["formats"]:
if not _fmt_available(fmt):
continue # wheel not installed here; the canonical gate covers it
data, title = D.render_document(tmpl, tmpl["sample"], fmt)
assert isinstance(data, (bytes, bytearray)) and data, f"{tmpl['id']}/{fmt} produced no bytes"
assert _valid(fmt, data), f"{tmpl['id']}/{fmt} invalid magic bytes"
@@ -310,8 +323,10 @@ def test_generate_from_fields_then_download_pdf_and_docx(client):
assert doc["id"] and doc["title"].startswith("Invoice")
assert set(doc["download"]) == set(inv["formats"])
for fmt, want_ct in (("pdf", "application/pdf"),
("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document")):
fmts = [("pdf", "application/pdf")]
if _fmt_available("docx"):
fmts.append(("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"))
for fmt, want_ct in fmts:
dl = client.get(doc["download"][fmt])
assert dl.status == 200
assert dl.headers["Content-Type"] == want_ct
@@ -322,6 +337,8 @@ def test_generate_from_fields_then_download_pdf_and_docx(client):
def test_generate_each_archetype_native_format(client):
for tid, fmt in (("resume", "pdf"), ("budget", "xlsx"), ("pitch_deck", "pptx")):
if not _fmt_available(fmt):
continue
t = D.get_template(tid)
doc = _gen(client, tid, fields=t["sample"]).json()
dl = client.get(doc["download"][fmt])