checking system…
Docs / back / tests/test_docs_links.py · line 1
Python · 72 lines
 1"""Check that ``src:path#L42`` links in docs/*.md actually point at real files.
 2
 3The docs were authored with hand-rolled line numbers. Code moves, line
 4numbers drift. This test surfaces the drift at CI time so we don't ship
 5broken navigation in the in-dashboard doc viewer.
 6
 7The contract enforced here:
 8
 9  * The file referenced by ``src:path`` exists.
10  * If a line number is supplied, it's within the file's line count.
11
12We don't try to verify that the SYMBOL on that line is still the one
13described in the doc — that would need a full AST cross-walk and is out
14of scope. Stale-but-still-valid links are caught on read.
15"""
16
17from __future__ import annotations
18
19import re
20from pathlib import Path
21
22import pytest
23
24
25_REPO = Path(__file__).resolve().parents[1]
26_DOCS_DIR = _REPO / "docs"
27_SRC_LINK_RE = re.compile(r"src:([A-Za-z0-9_./\-]+?)(?:#L(\d+))?(?=[\s\)#])")
28
29
30def _collect_links() -> list[tuple[Path, int, str, int | None]]:
31    """Walk every .md under docs/ and return (doc, lineno, target, line_anchor)."""
32    found: list[tuple[Path, int, str, int | None]] = []
33    for md in sorted(_DOCS_DIR.glob("*.md")):
34        for lineno, line in enumerate(md.read_text(encoding="utf-8").splitlines(), 1):
35            for m in _SRC_LINK_RE.finditer(line):
36                target = m.group(1)
37                anchor = int(m.group(2)) if m.group(2) else None
38                found.append((md, lineno, target, anchor))
39    return found
40
41
42# Use parametrize so each broken link is its own test failure — far easier
43# to read in CI output than one big concatenated assertion.
44_LINKS = _collect_links()
45
46
47@pytest.mark.parametrize(
48    "doc,doc_line,target,line_anchor",
49    _LINKS,
50    ids=[f"{p.name}:{ln}:{t}" + (f"#L{a}" if a else "") for p, ln, t, a in _LINKS],
51)
52def test_doc_src_link_resolves(doc: Path, doc_line: int, target: str, line_anchor: int | None) -> None:
53    target_path = _REPO / target
54    assert target_path.exists(), (
55        f"{doc.name}:{doc_line} → src:{target} does not exist. "
56        "If you renamed/moved the file, update the doc."
57    )
58    if line_anchor is not None:
59        total_lines = sum(1 for _ in target_path.open(encoding="utf-8", errors="replace"))
60        assert 1 <= line_anchor <= total_lines, (
61            f"{doc.name}:{doc_line} → src:{target}#L{line_anchor} is out of range "
62            f"(file has {total_lines} lines). Likely line numbers drifted; "
63            "either retarget or use a symbol-based pointer."
64        )
65
66
67def test_at_least_one_src_link_in_docs() -> None:
68    """Sanity: make sure the regex actually matched something. Catches the
69    case where someone renames the src: scheme and the test silently passes
70    with zero links found."""
71    assert len(_LINKS) > 0, "no src:path#L42 links found — did the link scheme change?"