# Test fixtures ## The PDF fixtures `two-line-krav.pdf` and `no-text-layer.pdf` are hand-written minimal PDFs, regenerated by `make_fixtures.py` in this directory: ``` python3 tests/fixtures/make_fixtures.py ``` They carry no library's output — the objects are laid out by hand and the xref offsets computed from the emitted bytes — so they are auditable byte for byte and reproducible from that one file. | Fixture | What it is for | |---|---| | `two-line-krav.pdf` | One heading plus one requirement row with label and value on the **same line**. That pairing is the property `pdfplumber` was chosen for. | | `no-text-layer.pdf` | A structurally valid page with no text operators — the shape a scanned or image-only PDF presents. Must fail fast (`extractor_empty_pdf`), never persist as an empty concept. | ## Why the expected PDF text is frozen as a literal `tests/test_extract.py` asserts the extracted text of `two-line-krav.pdf` as an exact string. That is deliberate, and it is the mechanism behind a promise this library makes everywhere else: - Extraction is deterministic **within** a parser version. Measured 2026-08-21 across five configurations, two runs each, compared byte for byte (`docs/2026-08-21-g2-pdf-extraction-measurement.md`). - Extraction is **not** guaranteed stable **across** parser versions. `pdfplumber` pins `pdfminer.six==20260107` exactly, and `pdfminer.six` ships date-stamped releases with no stability contract. So the real pin on extracted text is a transitive one, and it is exact. The consequence is worth stating plainly: **any golden fixture built on extracted PDF text is pinned to an exact parser version, and a parser upgrade is a fixture migration, not a routine bump.** The frozen literal is what makes that upgrade break something visible instead of drifting silently. If it goes red after a dependency change, the correct response is to read the diff and decide, not to re-record the expectation. The version range that carries this lives in `pyproject.toml`'s `[project.optional-dependencies] extract`, with the same reasoning at the declaration site. ## What these fixtures do not cover Structured table recovery. Measured on real Vegnormalene, only 45 of 196 detected table objects are clean enough to hand to `render_table` unchanged; two independent parsers return the same wrong shape, because the breakage is in the documents' ruling geometry rather than in either library. PDFs enter this library as **prose**, and structured tables are out of scope until that is decided separately.