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()