Expand documentation gate coverage
This commit is contained in:
parent
9023f28cc0
commit
95271dcf2e
1 changed files with 198 additions and 0 deletions
198
tests/test_docs_check.py
Normal file
198
tests/test_docs_check.py
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from tools.check_documentation import (
|
||||
COMMAND_REFERENCE_NOTICE,
|
||||
MAX_DIAGNOSTICS,
|
||||
DocumentationPolicy,
|
||||
check_documentation,
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCHEMA = ROOT / "schemas/reference-adapter.schema.json"
|
||||
|
||||
|
||||
class DocumentationCheckTests(unittest.TestCase):
|
||||
def repository(self) -> Path:
|
||||
root = Path(self.enterContext(tempfile.TemporaryDirectory()))
|
||||
(root / "docs").mkdir()
|
||||
(root / "schemas").mkdir()
|
||||
shutil.copyfile(SCHEMA, root / "schemas/reference-adapter.schema.json")
|
||||
(root / "pending.txt").write_text("", encoding="utf-8")
|
||||
return root
|
||||
|
||||
@staticmethod
|
||||
def policy(*required: str) -> DocumentationPolicy:
|
||||
return DocumentationPolicy(
|
||||
required_pages=tuple(Path(item) for item in required),
|
||||
pending_inventory=Path("pending.txt"),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def command_reference() -> str:
|
||||
return f"# DocForge command reference\n\n{COMMAND_REFERENCE_NOTICE}\n\n## CLI commands\n"
|
||||
|
||||
def test_valid_graph_h1_anchors_and_reference_adapter_toml_pass(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text(
|
||||
"# Documentation\n\n"
|
||||
"[Guide](docs/GUIDE.md#adapter-setup) and "
|
||||
"[commands](docs/COMMAND_REFERENCE.md). [Top](#documentation)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/GUIDE.md").write_text(
|
||||
"# Guide\n\n"
|
||||
"## Adapter *setup*\n\n"
|
||||
"Reference adapter configuration `.docforge/reference-adapter.toml`:\n\n"
|
||||
"```toml\n"
|
||||
"schema_version = 1\n"
|
||||
'project_id = "example-project"\n'
|
||||
'title = "Example"\n'
|
||||
'language = "python"\n'
|
||||
'source_roots = ["src"]\n'
|
||||
"```\n\n"
|
||||
"[Back](../README.md#documentation)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/COMMAND_REFERENCE.md").write_text(
|
||||
self.command_reference(),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=self.policy(
|
||||
"README.md",
|
||||
"docs/GUIDE.md",
|
||||
"docs/COMMAND_REFERENCE.md",
|
||||
),
|
||||
)
|
||||
|
||||
self.assertTrue(report.ok, [item.render() for item in report.diagnostics])
|
||||
self.assertEqual(3, report.markdown_pages)
|
||||
self.assertEqual(1, report.reference_adapter_examples)
|
||||
|
||||
def test_links_anchors_h1_and_generated_notice_fail_closed(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text(
|
||||
"# Documentation\n\n"
|
||||
"[bad anchor](docs/GUIDE.md#missing) "
|
||||
"[missing file](docs/ABSENT.md) "
|
||||
"[commands](docs/COMMAND_REFERENCE.md).\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/GUIDE.md").write_text(
|
||||
"# Guide\n\n# Duplicate\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/COMMAND_REFERENCE.md").write_text(
|
||||
"# DocForge command reference\n\n## CLI commands\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=self.policy(
|
||||
"README.md",
|
||||
"docs/GUIDE.md",
|
||||
"docs/COMMAND_REFERENCE.md",
|
||||
),
|
||||
)
|
||||
|
||||
self.assertFalse(report.ok)
|
||||
self.assertEqual(
|
||||
{"DOC016", "DOC017", "DOC020", "DOC028"},
|
||||
{item.code for item in report.diagnostics},
|
||||
)
|
||||
|
||||
def test_only_prior_milestone_records_are_reachability_exempt(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text("# Documentation\n", encoding="utf-8")
|
||||
(root / "docs/MILESTONE_2_BASELINE.md").write_text(
|
||||
"# Historical evidence\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/ORPHAN.md").write_text("# Orphan\n", encoding="utf-8")
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=self.policy("README.md"),
|
||||
)
|
||||
|
||||
orphans = [item.path for item in report.diagnostics if item.code == "DOC023"]
|
||||
self.assertEqual(["docs/ORPHAN.md"], orphans)
|
||||
|
||||
def test_pending_page_becomes_a_hard_failure_as_soon_as_it_exists(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text("# Documentation\n", encoding="utf-8")
|
||||
(root / "pending.txt").write_text("docs/FUTURE.md\n", encoding="utf-8")
|
||||
policy = self.policy("README.md", "docs/FUTURE.md")
|
||||
|
||||
pending = check_documentation(root, policy=policy)
|
||||
|
||||
self.assertTrue(pending.ok)
|
||||
self.assertEqual(1, pending.pending_pages)
|
||||
|
||||
(root / "docs/FUTURE.md").write_text("# Future\n", encoding="utf-8")
|
||||
stale = check_documentation(root, policy=policy)
|
||||
|
||||
self.assertIn("DOC014", {item.code for item in stale.diagnostics})
|
||||
self.assertIn("DOC023", {item.code for item in stale.diagnostics})
|
||||
|
||||
def test_unknown_pending_page_and_invalid_toml_are_reported(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text(
|
||||
"# Documentation\n\n[Adapter](docs/ADAPTER.md)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs/ADAPTER.md").write_text(
|
||||
"# Adapter\n\n"
|
||||
"```toml reference-adapter\n"
|
||||
"schema_version = 1\n"
|
||||
'project_id = "cpp-example"\n'
|
||||
'title = "C++"\n'
|
||||
'language = "cpp"\n'
|
||||
'source_roots = ["src"]\n'
|
||||
"```\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "pending.txt").write_text("docs/UNKNOWN.md\n", encoding="utf-8")
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=self.policy("README.md", "docs/ADAPTER.md"),
|
||||
)
|
||||
|
||||
self.assertIn("DOC012", {item.code for item in report.diagnostics})
|
||||
schema_failures = [item for item in report.diagnostics if item.code == "DOC026"]
|
||||
self.assertEqual(1, len(schema_failures))
|
||||
self.assertIn("compilation_database", schema_failures[0].message)
|
||||
|
||||
def test_diagnostics_are_deterministically_bounded(self) -> None:
|
||||
root = self.repository()
|
||||
(root / "README.md").write_text("# Documentation\n", encoding="utf-8")
|
||||
for index in range(MAX_DIAGNOSTICS + 7):
|
||||
(root / "docs" / f"ORPHAN_{index:03d}.md").write_text(
|
||||
f"# Orphan {index}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=self.policy("README.md"),
|
||||
)
|
||||
|
||||
self.assertEqual(MAX_DIAGNOSTICS, len(report.diagnostics))
|
||||
self.assertEqual(7, report.omitted_diagnostics)
|
||||
self.assertEqual(
|
||||
sorted(report.diagnostics),
|
||||
list(report.diagnostics),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue