-
Notifications
You must be signed in to change notification settings - Fork 1
testing
Now I have enough context to write the page.
- tests/test_repodocs.py
- tests/test_fix_publish.py
- tests/test_fix_generate.py
- tests/test_fix_plan.py
- tests/test_fix_diagrams.py
- tests/test_fix_scan_util.py
- tests/test_fix_render_links.py
- tests/test_fix_translate_cite.py
- tests/test_fix_citation_repair.py
The test suite lives entirely under tests/ and is run with pytest. It is split into two styles: a single large end-to-end selftest() function (tests/test_repodocs.py) that exercises the whole pipeline through temporary repos, and a set of test_fix_*.py files that each hold focused pytest-style regression tests tied to specific code-review fixes (mostly from PR #2).
Sources: tests/test_repodocs.py:L1-L27, tests/test_fix_publish.py:L1-L14
selftest() is the ported former in-module selftest of repodocs. It is invoked by the single pytest entry point test_repodocs() at the bottom of the file:
def test_repodocs():
selftest()Sources: tests/test_repodocs.py:L589-L590
It runs as one long sequence of assertions against temporary directories (tempfile.TemporaryDirectory()), rather than many small pytest test functions, and imports directly from the repodocs package modules (plan, render, _util, backend, citations, cli, generate, gitlinks, publish, scan, translate). Two small local helpers, _raises and _raises_exit, assert that a callable raises ValueError or exits with a non-zero SystemExit code respectively.
Sources: tests/test_repodocs.py:L12-L41
selftest() covers, in order:
| Area | What it asserts |
|---|---|
plan_pages / scan / scan_inventory
|
mandatory slugs (overview, installation, architecture, development, changelog) appear; overview is first; a component-* page exists; VERSION matches semver |
validate_pages |
bad slugs, non-dict entries, and duplicate slugs are dropped/deduped; missing candidate files fall back to README.md
|
parse_pages |
extracts a JSON array from a fenced code block, raises on unparseable text |
component-* page thresholds |
large files produce a component page, small ones don't |
CITATION_RE |
matches [path:Lx-Ly](path#Lx-Ly) citation links, not plain markdown links |
compute_file_hash |
known bytes hash to a known sha256 |
generate_decision |
pure generate/skip decision logic (new, unchanged, changed, added, removed) |
llm_plan idempotency |
skip on matching fingerprint, replan on inventory change, no cache written on planner fallback, retried on next run |
graph_digest |
empty digest with no graphify-out/graph.json or corrupt JSON; compact digest with node/edge counts otherwise |
decide_page |
memoizes file hashes across pages via a shared hcache dict |
build_html |
embeds page titles/markdown, excludes index.md, dies cleanly (SystemExit) on an empty dir or network failure in vendor_assets
|
_github_slug / rewrite_citation_links
|
GitHub remote-URL parsing and citation-to-blob-URL rewriting, including HTML/attribute escaping of injected content |
citations_safe |
untracked (??) files under the output dir are harmless; anything else untracked, or a dirty tree, is unsafe |
group_pages |
deterministic nav grouping into Overview/Features/Reference/Development |
translate_prompt / lang_labels
|
prompt preserves source markdown verbatim and names the target language; label lookup falls back to English |
jobs_count |
REPODOCS_JOBS parsing/clamping (default 4, range 1–16, invalid input falls back to 4) |
backend_name / effective_model / backend_contract
|
backend selection from REPODOCS_BACKEND, model resolution/override via REPODOCS_MODEL, and planner/writer contract text selection |
stage_publish / staged_secret_findings / publish_branch_safe
|
staging layout (index.html, .nojekyll, translated subdirs), secret scanning, protected-branch rejection |
stage_wiki |
Home.md mapping, _Sidebar.md ordering/titles, absolute citation rewriting, index.md exclusion, index.md-as-Home fallback |
| Symlink rejection |
stage_publish/stage_wiki reject symlinked source files before any read or copy |
wiki_remote_url / publish_wiki_push
|
wiki remote URL derivation; full clone/commit/push orchestration against a local bare git remote covering WikiNotInitialized, no-op push, normal push, and push failure |
setup_install |
fresh install, up-to-date, local-diverges-kept, --force overwrite |
safe_repo_file |
rejects traversal, absolute paths, NUL bytes, symlink escapes; accepts real in-repo files |
validate_pages traversal |
a ../../../etc/passwd candidate is dropped, falling back to README.md
|
scan self-ingestion |
repo-docs/, graphify-out/, and any configured out dir never leak into src_files; symlink escapes dropped |
citation_error |
valid citation passes; range mismatch, path mismatch, zero start, reversed range, beyond-EOF, missing file, traversal, and malformed href are all caught |
requires_evidence / citation_problems
|
prose needs a citation; a heading + file list alone does not; unsourced or invalid-citation pages block, index.md is excluded |
| XSS wiring | built HTML viewer sanitizes markdown via DOMPurify.sanitize(marked.parse(...)), fails closed if the sanitizer fails to load, loads purify.min.js before use, and runs Mermaid in "strict" security mode |
THIRD_PARTY_NOTICES |
names every vendored library (marked, Mermaid, highlight.js, DOMPurify) and its SPDX license identifier |
Every temporary fixture is created with tempfile.TemporaryDirectory() and torn down automatically; no test in this file touches the real filesystem outside its own tempdir except through explicit subprocess calls to git inside another tempdir.
Sources: tests/test_repodocs.py:L396-L468
Each test_fix_*.py module targets a specific repodocs submodule and documents, in its module docstring, which review comments or PR it addresses. Unlike test_repodocs.py, these use ordinary pytest fixtures (tmp_path, monkeypatch, capsys, pytest.raises) and one assertion (or a few) per test function.
Covers protected-branch normalization (publish_branch_safe treats refs/heads/main/MAIN as unsafe), an ENCRYPTED PRIVATE KEY gap in PUBLISH_SECRET_PATTERNS, repeat-publish to the same branch not colliding with a leftover orphan ref, symlink rejection in _reject_symlink_dest (both file and parent-dir symlinks) before publish_wiki_push copies anything, clone-failure classification (WikiNotInitialized vs. WikiPublishError), an index-only wiki reaching Home.md via cmd_publish_wiki, citation scanning happening only after symlink rejection in staging, and _publishable_subdir_mds skipping a translated subdir that has no rendered wiki.html yet.
Sources: tests/test_fix_publish.py:L1-L27, tests/test_fix_publish.py:L48-L240
Covers _safe_page rejecting a traversal slug (../evil) and cmd_generate refusing to write outside out even if an unsafe slug slips past plan.py's own validation, decide_page skipping a candidate file that escapes the repo (via symlink or ../) before hashing it, page_prompt omitting the graphify-out/graph.json hint when that file is a symlink escaping the repo (but including it for a real in-repo graph), decide_page falling back to "generate" on a corrupt .hashes.json record instead of crashing, index.md listing the full plan even when --pages filters generation to a subset, and _gitignore_notice not raising when out == repo.
Sources: tests/test_fix_generate.py:L1-L8, tests/test_fix_generate.py:L17-L146
Covers graph_digest surviving invalid UTF-8 in graph.json, validate_pages handling files: null instead of crashing, load_plan re-validating a cached plan.json (rejecting a traversal slug) and recovering from a corrupt cache, llm_plan falling back to the heuristic planner (and not caching a fingerprint) when the LLM's plan is missing the mandatory installation slug, and plan_pages generating collision-safe component-* slugs for directory names that normalize to the same slug (Foo Bar vs. foo-bar).
Sources: tests/test_fix_plan.py:L1-L5, tests/test_fix_plan.py:L12-L93
Covers process_page swapping a rendered mermaid block for an image embed on success, keeping the block verbatim on render failure, numbering multiple blocks in one page, and _poster_yaml correctly indenting mermaid source and numbering "DIAGRAM n/total" for multi-diagram pages. The Bun renderer is stubbed via monkeypatch.setattr(dg, "_render_png", ...) so no Bun/Playwright process is spawned.
Sources: tests/test_fix_diagrams.py:L1-L46
Covers dangling symlinks and FIFOs being excluded from src_files, a nested --out directory pruning only its own subtree, escaping symlinks for README.md, manifest files, and CI workflow files being dropped, CI workflow lists being sorted, and safe_repo_file returning None on a self-referencing symlink loop (ELOOP).
Sources: tests/test_fix_scan_util.py:L1-L4, tests/test_fix_scan_util.py:L13-L112
Covers render_html HTML-escaping a malicious breadcrumb inside <title> and inside the inline REPO = ... script assignment (with </ escaped to <\/), escaping a malicious ghroot href/text, CDN_ASSETS being pinned to exact semver with sha384- SRI hashes wired into <script integrity=...>/<link integrity=...> tags (and vendored local assets omitting that SRI hash), rewrite_citation_links escaping a malicious base URL rather than injecting a live attribute, _github_slug rejecting HTML metacharacters in a repo name, citations_safe rejecting an untracked source file outside the output dir, and the codex backend emitting its "read-only... does NOT restrict reads... untrusted repositories" warning exactly once per process.
Sources: tests/test_fix_render_links.py:L1-L11, tests/test_fix_render_links.py:L15-L133
Covers localize_headings skipping a heading inside a fenced code block while translating a real ## Relevant source files heading, translate_plan_file falling back (without calling the LLM) on a non-list plan.json, cmd_translate rejecting path-like language codes (.., ., ../x, a/b, /etc) with SystemExit(1), propagating a plan-translation failure as a nonzero return code while still writing the fallback copy, and blocking (vs. allowing) a translated page depending on whether it dropped the Sources: citation line. The citations half covers requires_evidence exempting the ## Relevant source files bullet list but flagging any other bare bullet list, and citation_problems flagging pages with no citation, rejecting a citation-shaped label with no linked href, ignoring citations hidden inside HTML comments or code fences, and accepting a real rendered citation.
Sources: tests/test_fix_translate_cite.py:L1-L10, tests/test_fix_translate_cite.py:L14-L191
Covers repair_citations clamping an end-of-file line-range overshoot (e.g. a 5-line file cited to L6) down to the real file length, leaving an already-valid citation untouched, leaving a hallucinated missing-file citation untouched, leaving a citation whose start line is already past EOF untouched (not a clamp case), resolving a dropped path prefix via a unique tracked git suffix match (e.g. bare mod.py resolving to src/pkg/mod.py), combining prefix resolution with overshoot clamping in one pass, adding a missing L prefix to a range label, normalizing a single-line citation (L3) to a L3-L3 range, and leaving a citation alone when the label path and href path disagree (ambiguous — not guessed).
Sources: tests/test_fix_citation_repair.py:L1-L3, tests/test_fix_citation_repair.py:L9-L74
All test modules are plain pytest files under tests/; none define a custom runner or if __name__ == "__main__" block. They are run with:
pytest
or, to target a single module:
pytest tests/test_repodocs.py
test_repodocs.py's single test_repodocs() entry point runs the entire selftest() sequence; a failure anywhere inside it surfaces as a single failing test with an AssertionError at the specific failing assert line. The test_fix_*.py files instead expose one pytest test function per regression case, so pytest -k <name> can target an individual fix.
Sources: tests/test_repodocs.py:L44-L590
Tests that need a real git repository (publish/wiki push paths) create one in a tmp_path/tempfile.TemporaryDirectory() and set GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL/GIT_COMMITTER_NAME/GIT_COMMITTER_EMAIL via monkeypatch.setenv (or by mutating os.environ directly in test_repodocs.py) so commits do not depend on the machine's global git configuration.
Sources: tests/test_fix_publish.py:L36-L46, tests/test_repodocs.py:L396-L402
Network- and subprocess-dependent code paths (LLM backends, Bun-based diagram rendering, git clone/push) are stubbed with monkeypatch.setattr rather than exercised for real: plan_mod.run_llm, render_mod.vendor_assets, dg._render_png, translate_mod.run_llm/parallel_llm, gen.parallel_llm, and publish_mod.subprocess.run/tempfile.mkdtemp are all replaced with fakes in the relevant tests.
Sources: tests/test_repodocs.py:L112-L127, tests/test_repodocs.py:L184-L191, tests/test_fix_diagrams.py:L8-L17, tests/test_fix_generate.py:L10-L14, tests/test_fix_publish.py:L126-L130
- Home
- Installation & Setup
- Architecture
- CLI Reference
- LLM Backend Selection
- Repository Scanning
- Wiki Page Planning
- Page Generation
- Source Citations
- Translation
- HTML Rendering
- Diagram Rendering
- Diagram Poster Tool
- Git Remote Link Resolution
- Publishing
- GitHub Wiki Integration
- Shared Utilities
- Environment Configuration
- Security & Trust Boundaries
- Limitations & Non-goals
- Testing
- Development
- Contributing
- Upgrading
- Changelog