"""Tests use isolated temporary fixtures and require only Python's standard library.""" from pathlib import Path import sys import tempfile import unittest sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from check_docs import check_repository, heading_slug class DocumentationChecks(unittest.TestCase): def setUp(self): self.directory = tempfile.TemporaryDirectory() self.addCleanup(self.directory.cleanup) self.root = Path(self.directory.name) self.write("README.md", "# Entry\n") def write(self, name, content): path = self.root / name path.parent.mkdir(parents=True, exist_ok=True) path.write_text(content, encoding="utf-8") def errors(self): return check_repository(self.root).errors def test_valid_local_link_and_unicode_anchor(self): self.write("README.md", "# Entry\n[Topic](topic.md#用户身份)\n") self.write("topic.md", "# 用户身份\n") self.assertEqual(self.errors(), []) def test_missing_file_is_reported(self): self.write("README.md", "# Entry\n[Missing](absent.md)\n") self.assertTrue(any("missing link target" in item for item in self.errors())) def test_missing_anchor_is_reported(self): self.write("README.md", "# Entry\n[Missing](topic.md#absent)\n") self.write("topic.md", "# Topic\n") self.assertTrue(any("missing heading anchor" in item for item in self.errors())) def test_template_links_inside_fences_are_ignored(self): self.write("README.md", "# Entry\n````markdown\n[Placeholder](none.md)\n```text\nexample\n```\n````\n") self.assertEqual(self.errors(), []) def test_unclosed_fence_is_reported(self): self.write("README.md", "# Entry\n```text\nexample\n") self.assertTrue(any("unclosed code fence" in item for item in self.errors())) def test_duplicate_heading_anchor_suffix(self): self.write("README.md", "# Entry\n[Second](topic.md#topic-1)\n") self.write("topic.md", "# Topic\n## Topic\n") self.assertEqual(self.errors(), []) def test_external_link_is_not_fetched(self): self.write("README.md", "# Entry\n[External](https://example.invalid/path#missing)\n") self.assertEqual(self.errors(), []) self.assertEqual(check_repository(self.root).links, 0) def test_machine_specific_link_is_rejected(self): self.write("README.md", "# Entry\n[Local](/absolute/local.md)\n") self.assertTrue(any("absolute link" in item for item in self.errors())) def test_escape_from_repository_is_rejected(self): self.write("README.md", "# Entry\n[Outside](../outside.md)\n") self.assertTrue(any("escapes repository" in item for item in self.errors())) def test_windows_absolute_link_is_rejected(self): self.write("README.md", "# Entry\n[Local](C:/workspace/local.md)\n") self.assertTrue(any("absolute link" in item for item in self.errors())) def test_protocol_relative_web_link_is_not_fetched(self): self.write("README.md", "# Entry\n[External](//example.invalid/path)\n") self.assertEqual(self.errors(), []) def test_file_uri_is_rejected(self): self.write("README.md", "# Entry\n[Local](file:///workspace/local.md)\n") self.assertTrue(any("absolute link" in item for item in self.errors())) def test_invalid_utf8_is_reported_without_crashing(self): (self.root / "README.md").write_bytes(b"\xff") self.assertTrue(any("cannot read UTF-8" in item for item in self.errors())) def test_rule_id_mismatch_is_reported(self): self.write("authoring/rules/DOC-001.md", "# DOC-002 Wrong identity\n") self.assertTrue(any("heading ID differ" in item for item in self.errors())) def test_source_id_mismatch_is_reported(self): self.write("authoring/research/SRC-01.md", "# SRC-02 Wrong identity\n") self.assertTrue(any("heading ID differ" in item for item in self.errors())) def guide_fixture(self, code): self.write("README.md", "# Entry\n[Guide](guide/01-example.md)\n") self.write("guide/01-example.md", "# Guide\n```endorphin\n" + code + "\n```\n") def test_valid_use(self): self.guide_fixture("use alice:shop.pricing") self.assertEqual(self.errors(), []) def test_use_without_user_separator_is_rejected(self): self.guide_fixture("use alice.shop.pricing") self.assertTrue(any("complete namespace key" in item for item in self.errors())) def test_selective_import_is_rejected(self): self.guide_fixture("use alice:shop.ui::{Column}") self.assertTrue(any("complete namespace key" in item for item in self.errors())) def test_source_namespace_declaration_is_rejected(self): self.guide_fixture("namespace alice:shop") self.assertTrue(any("own namespace" in item for item in self.errors())) def test_gapped_chapter_sequence_is_rejected(self): self.write("guide/02-example.md", "# Guide\n") self.assertTrue(any("chapter numbering" in item for item in self.errors())) def test_heading_punctuation_is_removed(self): self.assertEqual(heading_slug("4.4 名称引用"), "44-名称引用") if __name__ == "__main__": unittest.main()