gabriel / musehub public
test_gen_mcp_reference.py python
99 lines 4.1 KB
Raw
sha256:b3e654170421616945cf1599d6bb4e8e219c3c2189458ed7f41af4b2d685f87a docs: regenerate docs/reference/mcp.md's tool/resource cata… Sonnet 5 patch 2 days ago
1 """TDD — musehub#209: docs/reference/mcp.md tool/resource counts were stale.
2
3 Guards against re-drift: ``docs/reference/mcp.md``'s generated regions
4 (tool/resource catalogues + their counts) must always match what
5 ``scripts/gen_mcp_reference.py`` would produce from the live registries.
6 If this test fails, someone added/removed a tool or resource and forgot
7 to run ``python scripts/gen_mcp_reference.py``.
8 """
9 from __future__ import annotations
10
11 import sys
12 from pathlib import Path
13
14 import pytest
15
16 sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
17 import gen_mcp_reference as gen # noqa: E402
18
19
20 @pytest.fixture()
21 def committed_doc() -> str:
22 return gen._DOC_PATH.read_text(encoding="utf-8")
23
24
25 class TestDocStaysInSync:
26 def test_committed_doc_matches_generated_output(self, committed_doc: str) -> None:
27 """The real regression guard: committed mcp.md == freshly generated."""
28 assert gen.generate(committed_doc) == committed_doc
29
30 def test_tool_counts_match_live_registry(self, committed_doc: str) -> None:
31 from musehub.mcp.tools import MCP_TOOLS
32
33 assert f"Tools — {len(MCP_TOOLS)} total" in committed_doc
34
35 def test_resource_counts_match_live_registry(self, committed_doc: str) -> None:
36 from musehub.mcp.resources import STATIC_RESOURCES, RESOURCE_TEMPLATES
37
38 total = len(STATIC_RESOURCES) + len(RESOURCE_TEMPLATES)
39 assert f"Resources — {total} total" in committed_doc
40
41 def test_every_live_tool_is_documented(self, committed_doc: str) -> None:
42 from musehub.mcp.tools import MCP_TOOLS
43
44 missing = [t["name"] for t in MCP_TOOLS if f"#### `{t['name']}`" not in committed_doc]
45 assert not missing, f"{len(missing)} live tools missing from mcp.md: {missing[:10]}"
46
47 def test_every_live_static_resource_is_documented(self, committed_doc: str) -> None:
48 from musehub.mcp.resources import STATIC_RESOURCES
49
50 missing = [r["uri"] for r in STATIC_RESOURCES if f"#### `{r['uri']}`" not in committed_doc]
51 assert not missing, f"missing static resources: {missing}"
52
53 def test_every_live_resource_template_is_documented(self, committed_doc: str) -> None:
54 from musehub.mcp.resources import RESOURCE_TEMPLATES
55
56 missing = [
57 r["uriTemplate"] for r in RESOURCE_TEMPLATES
58 if f"#### `{r['uriTemplate']}`" not in committed_doc
59 ]
60 assert not missing, f"missing resource templates: {missing}"
61
62
63 class TestGeneratorUnit:
64 def test_param_table_escapes_literal_pipes(self) -> None:
65 schema = {
66 "properties": {"x": {"type": "string", "description": "a | b"}},
67 "required": ["x"],
68 }
69 rendered = gen._param_rows(schema)
70 assert "a \\| b" in rendered
71 # Every data row must have exactly 5 unescaped pipe characters
72 # (leading + 4 separators + trailing) or it breaks table rendering.
73 for line in rendered.splitlines():
74 if line.startswith("| `"):
75 unescaped = line.replace("\\|", "")
76 assert unescaped.count("|") == 5, line
77
78 def test_param_table_empty_schema_has_no_parameters_note(self) -> None:
79 assert gen._param_rows({"properties": {}}) == "_No parameters._"
80
81 def test_tool_classification_is_a_partition(self) -> None:
82 from musehub.mcp.tools import MCP_TOOLS
83
84 read, write, elicit = gen._classify_tools()
85 assert len(read) + len(write) + len(elicit) == len(MCP_TOOLS)
86 names = [t["name"] for t in (*read, *write, *elicit)]
87 assert len(names) == len(set(names)) # no tool double-counted
88
89 def test_check_passes_against_committed_doc(self, capsys: pytest.CaptureFixture[str]) -> None:
90 """Exercises the --check code path end-to-end via main(), matching
91 how the script is actually invoked (python scripts/gen_mcp_reference.py --check)."""
92 import sys as _sys
93 old_argv = _sys.argv
94 try:
95 _sys.argv = ["gen_mcp_reference.py", "--check"]
96 gen.main()
97 finally:
98 _sys.argv = old_argv
99 assert "up to date" in capsys.readouterr().out
File History 1 commit
sha256:b3e654170421616945cf1599d6bb4e8e219c3c2189458ed7f41af4b2d685f87a docs: regenerate docs/reference/mcp.md's tool/resource cata… Sonnet 5 patch 2 days ago