Files
VoiceStudio/tests/test_structure_doc.py
Palash DebnathandClaude Opus 5 a5f4d61eda docs: keep STRUCTURE.md's counts honest with a test
Lands #1981 by @Dawcraft, which refreshes docs/STRUCTURE.md to match the tree
as it actually is — the old file still described a root-level layout that the
2026-07-12 cleanup removed, and pointed at a tests/services/ mirror that has
not existed since the tests/backend/ reorganisation.

Verified every path, directory and CI claim in the refreshed file against the
repo: the router auto-include list, the isolated backend/tests/ pytest step,
the smoke-matrix job and its HF_HUB_OFFLINE guard, and every file the tree
names. One number was off — backend/services/ holds 78 modules, not 79.

Off-by-one in a doc is the symptom; the class is a count nothing checks, which
is wrong the week after it is written. tests/test_structure_doc.py now pins
the router count, the service count and the engine-adapter list to the tree,
so the next module to land fails the suite with the line to update instead of
quietly aging the doc. Fails before the fix (79 != 78), passes after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ypcgSsh5j2PEonSJiAU1S
2026-09-09 23:06:59 -07:00

56 lines
2.2 KiB
Python

"""`docs/STRUCTURE.md` carries counts of the trees it describes.
Counts rot silently: the doc shipped "79 modules of business logic" when
`backend/services/` held 78, and nothing failed. A number in a doc that no
test reads is a number that is wrong the week after it is written, so the
counts are pinned here rather than trusted to review attention.
Per CLAUDE.md's token-economy rule, mechanical claims like this belong in a
deterministic test, not in agent or reviewer effort. When you add a router or
a service, update the doc — this test tells you which line.
"""
import re
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
STRUCTURE = REPO / "docs" / "STRUCTURE.md"
def _modules(directory: Path) -> set:
"""Importable modules directly under `directory`: flat `*.py` files plus
subpackages. `__init__.py` is packaging, not a module of its own."""
names = {p.stem for p in directory.glob("*.py") if p.stem != "__init__"}
names |= {p.name for p in directory.iterdir() if (p / "__init__.py").is_file()}
return names
def _documented(pattern: str) -> int:
text = STRUCTURE.read_text(encoding="utf-8")
match = re.search(pattern, text)
assert match, f"docs/STRUCTURE.md no longer states a count for {pattern!r}"
return int(match.group(1))
def test_router_count_matches_the_tree():
actual = len(_modules(REPO / "backend" / "api" / "routers"))
assert _documented(r"(\d+) routers, auto-included") == actual, (
f"docs/STRUCTURE.md says N routers; backend/api/routers/ has {actual}"
)
def test_service_count_matches_the_tree():
actual = len(_modules(REPO / "backend" / "services"))
assert _documented(r"(\d+) modules of business logic") == actual, (
f"docs/STRUCTURE.md says N services; backend/services/ has {actual}"
)
def test_every_engine_adapter_is_listed():
engines = REPO / "backend" / "engines"
actual = {p.name for p in engines.iterdir() if (p / "__init__.py").is_file()}
text = STRUCTURE.read_text(encoding="utf-8")
listed = text[text.index("per-engine adapters:") :][:400]
missing = sorted(name for name in actual if name not in listed)
assert not missing, f"docs/STRUCTURE.md does not list engine adapter(s): {missing}"