Add strict documentation graph gate
This commit is contained in:
parent
983a55f156
commit
9023f28cc0
4 changed files with 893 additions and 2 deletions
176
tests/test_documentation_check.py
Normal file
176
tests/test_documentation_check.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from tools.check_documentation import (
|
||||
DocumentationPolicy,
|
||||
check_documentation,
|
||||
)
|
||||
|
||||
|
||||
class DocumentationCheckTests(unittest.TestCase):
|
||||
def repository(self, parent: Path) -> Path:
|
||||
root = parent / "repository"
|
||||
(root / "docs").mkdir(parents=True)
|
||||
(root / "schemas").mkdir()
|
||||
(root / "schemas" / "reference-adapter.schema.json").write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"project_id",
|
||||
"title",
|
||||
"language",
|
||||
"source_roots",
|
||||
],
|
||||
"properties": {
|
||||
"schema_version": {"const": 1},
|
||||
"project_id": {"type": "string"},
|
||||
"title": {"type": "string"},
|
||||
"language": {"enum": ["python", "javascript", "typescript", "cpp"]},
|
||||
"source_roots": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
"compilation_database": {"type": "string"},
|
||||
},
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
return root
|
||||
|
||||
def test_linked_pages_h1_anchors_and_reference_example_pass(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.repository(Path(directory))
|
||||
(root / "README.md").write_text(
|
||||
"# Project\n\n[Guide](docs/GUIDE.md#reference-adapter-configuration)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs" / "GUIDE.md").write_text(
|
||||
"\n".join(
|
||||
(
|
||||
"# Guide",
|
||||
"",
|
||||
"## Reference adapter configuration",
|
||||
"",
|
||||
"`.docforge/reference-adapter.toml`:",
|
||||
"",
|
||||
"```toml",
|
||||
"schema_version = 1",
|
||||
'project_id = "fixture"',
|
||||
'title = "Fixture"',
|
||||
'language = "python"',
|
||||
'source_roots = ["src"]',
|
||||
"```",
|
||||
"",
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=DocumentationPolicy(
|
||||
required_pages=(Path("README.md"), Path("docs/GUIDE.md")),
|
||||
pending_inventory=None,
|
||||
),
|
||||
)
|
||||
|
||||
self.assertTrue(report.ok, [item.render() for item in report.diagnostics])
|
||||
self.assertEqual(2, report.markdown_pages)
|
||||
self.assertEqual(1, report.reference_adapter_examples)
|
||||
|
||||
def test_missing_anchor_orphan_and_duplicate_h1_fail_deterministically(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.repository(Path(directory))
|
||||
(root / "README.md").write_text(
|
||||
"# Project\n\n[Broken](docs/GUIDE.md#missing)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs" / "GUIDE.md").write_text(
|
||||
"# Guide\n\n# Duplicate\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs" / "ORPHAN.md").write_text("# Orphan\n", encoding="utf-8")
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=DocumentationPolicy(
|
||||
required_pages=(Path("README.md"), Path("docs/GUIDE.md")),
|
||||
pending_inventory=None,
|
||||
),
|
||||
)
|
||||
|
||||
self.assertFalse(report.ok)
|
||||
codes = [item.code for item in report.diagnostics]
|
||||
self.assertEqual(["DOC016", "DOC017", "DOC023"], sorted(codes))
|
||||
|
||||
def test_pending_inventory_allows_absence_but_rejects_existing_page(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.repository(Path(directory))
|
||||
(root / "README.md").write_text("# Project\n", encoding="utf-8")
|
||||
pending_path = root / "pending.txt"
|
||||
pending_path.write_text("docs/FUTURE.md\n", encoding="utf-8")
|
||||
policy = DocumentationPolicy(
|
||||
required_pages=(Path("README.md"), Path("docs/FUTURE.md")),
|
||||
pending_inventory=Path("pending.txt"),
|
||||
)
|
||||
|
||||
missing = check_documentation(root, policy=policy)
|
||||
self.assertTrue(missing.ok)
|
||||
self.assertEqual(1, missing.pending_pages)
|
||||
|
||||
(root / "docs" / "FUTURE.md").write_text("# Future\n", encoding="utf-8")
|
||||
stale = check_documentation(root, policy=policy)
|
||||
self.assertEqual(["DOC014", "DOC023"], [item.code for item in stale.diagnostics])
|
||||
|
||||
def test_invalid_reference_example_reports_schema_path(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = self.repository(Path(directory))
|
||||
(root / "README.md").write_text(
|
||||
"# Project\n\n[Guide](docs/GUIDE.md)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(root / "docs" / "GUIDE.md").write_text(
|
||||
"\n".join(
|
||||
(
|
||||
"# Guide",
|
||||
"",
|
||||
"Reference adapter configuration:",
|
||||
"",
|
||||
"```toml",
|
||||
'language = "python"',
|
||||
'source_roots = ["src"]',
|
||||
"```",
|
||||
"",
|
||||
)
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
report = check_documentation(
|
||||
root,
|
||||
policy=DocumentationPolicy(
|
||||
required_pages=(Path("README.md"), Path("docs/GUIDE.md")),
|
||||
pending_inventory=None,
|
||||
),
|
||||
)
|
||||
|
||||
schema_errors = [item for item in report.diagnostics if item.code == "DOC026"]
|
||||
self.assertEqual(3, len(schema_errors))
|
||||
messages = " ".join(item.message for item in schema_errors)
|
||||
self.assertIn("schema_version", messages)
|
||||
self.assertIn("project_id", messages)
|
||||
self.assertIn("title", messages)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue