Testing¶
The test suite lives in tests/, outside the pplx_export package, and runs fully offline — all input data is committed under tests/fixtures/, and no test ever touches the network or a real user-level config. This page covers running the suite, its layout, and how to add tests. For the mechanism view (how snapshot regression interlocks with the renderer's idempotency promise) see Test system architecture; for the input data itself see Test fixtures.
Running the tests¶
uv run pytest tests
pytest is a declared dev dependency, so uv run needs no extra setup. What you can rely on:
- Zero network — raw API captures are checked in; online code paths are exercised through offline re-rendering or
tmp_path/monkeypatchconstructions. - No real user config — an autouse fixture (
tests/conftest.py:40-53) loads a placeholder account config (alice/bob, aligned withconfig.example.toml), so results never depend on a real~/.config. - Fast — 300+ tests in about a second; the case count keeps growing as regressions are added.
Useful selections:
| Command | Effect |
|---|---|
uv run pytest tests |
full suite |
uv run pytest tests/test_units.py |
one file |
uv run pytest tests -k snapshot |
only tests whose name matches snapshot |
uv run pytest tests -x -q |
stop at the first failure, quiet output |
Suite layout¶
The suite has three families:
- Render snapshot tests —
test_render_snapshots.py: end-to-end regression from fixture raw JSON through normalize → parse → render, compared byte-for-byte against committed golden snapshots. Covers all five modes (search / deep-research / computer / council / study) plus trimmed defect scenarios. - Unit tests —
test_units.pyand the topical files: class/function-level behavior of state, throttling, incremental planning, normalization, and the maintenance commands. - Fix-regression files —
test_fix_*.py: one file per external-review finding (N-xx / V3-xx / V4-xx / V5-xx); each file's header docstring recaps the finding, and the test pins the fixed behavior.
| File | Coverage |
|---|---|
conftest.py |
shared fixtures: render_fixture / rendered; placeholder user config |
test_render_snapshots.py |
render snapshot regression: five-mode full-thread fixtures + trimmed defect scenarios; byte-for-byte golden comparison; dict-repr residue signature check |
test_units.py |
BatchState (trailing-zero normalization, expired terminal state, corrupt backup), Throttle (backoff formula/cap/reset), plan_incremental, AssetDownloader._final_name, normalize_math_delims, TestDetectMode, TestSafeFolder, TestFetchMissingBlocks, TestExternalReviewFixes |
test_interruptions.py |
interruption semantics: classify_wf_status five classes, attribution waterfall ③ appendix, render annotation, interruption registration |
test_stub_workflows.py |
stub-turn association: match_stub_workflows (10 s window, single consumption, anchored exclusion), render_wf_item nested-rendering recursion/dedup guard |
test_relations.py |
relations graph: same_prompt / references / subagent_of edge construction |
test_search_mode_backfill.py |
search-mode-backfill: specificity priority, original values preserved, idempotent resume, index merge |
test_sync_deleted.py |
sync-deleted state machine: cross-account candidate determination, terminal-state handling |
test_answer_variants.pytest_answer_variant_logging.py |
answer_variants detection chain and ANSWER_VARIANT_DETECTED jsonl logging |
test_config_external.py |
user-level external config: load priority, missing-file degradation, resolve_cli_account |
test_fix_n01…n11_*.py (9 files) |
second-round review regressions (N-01..N-12) |
test_fix_v301_*.py / test_fix_v305_*.py |
third-round review regressions (V3-01 / V3-05) |
test_fix_v4*.py |
fourth-round review regressions (V4-01..V4-06) |
test_fix_v5_review.py |
fifth-round review regressions (V5-01..V5-10) |
How the snapshot tests reuse the production rerender path¶
The snapshot tests contain no re-implemented pipeline — they call the production renderer:
render_fixture(tests/conftest.py:56-68) copies the fixture's raw JSON (raw_entries.json/raw_blocks.json/thread.json) into a temporary directory and callsrerender(pplx_export/commands/rerender_cmd.py:105) — the same function thepplx-export re-rendercommand runs in production.- The
renderedfixture (tests/conftest.py:71-78) is a factory:rendered("search_demo")returns(re-rendered thread dir, golden dir in fixtures). The test then diffsconversation.mdand everyturns/turn_*.mdbyte-for-byte. - Because the goldens are regenerated through the same offline path (see Test fixtures), the tests always diff fresh renderer output against the committed products: any render-layer change that alters artifact bytes turns the suite red — interlocked with the re-render idempotency promise.
Beyond byte equality, test_render_snapshots.py pins content invariants: answers are never the empty placeholder (无), and a dict-repr residue signature ({'type': ... leaking into rendered text) fails the test.
Adding a test¶
- Unit test for existing logic — add a
test_*function to the matching topical file (or aTest*class intest_units.py). Usetmp_path/monkeypatch; never the network, never a real~/.config(the placeholder config is autouse). - Regression for a bug fix — create
tests/test_fix_<round><nn>_<slug>.pywith a header docstring recapping the finding (old behavior → fixed behavior), then pin the fixed behavior. Construct raw JSON directly intmp_path, as the existingtest_fix_*files do. - Render regression — you need a fixture: add (or trim) one under
tests/fixtures/, refresh its golden with the maintenance tool (see Test fixtures), then either add the directory name toFULL/SCENARIOSintest_render_snapshots.pyor write a dedicatedtest_*function consuming therenderedfixture for scenario-specific assertions.
Match the house style: type annotations, from __future__ import annotations, and bilingual module docstrings like the neighboring files.
See also¶
- Test fixtures — the synthetic raw captures and golden snapshots the suite runs on
- Test system architecture — the regression strategy behind the suite
- Offline operations — the production re-render pipeline the snapshot tests reuse