from __future__ import annotations import sys import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT / "dev_scripts")) from check_harness import ( # noqa: E402 CORE_DOCUMENT_REQUIREMENTS, CORE_PAGE_PATHS, REQUIRED_FILES, check_claude_code_entry, check_core_documents, check_agent_efficiency_rules, check_task_template, core_mapping_errors, missing_sections, ) from wiki_docs import load_config # noqa: E402 class CoreDocumentTests(unittest.TestCase): def test_current_core_documents_have_required_sections(self) -> None: errors: list[str] = [] check_core_documents(errors) self.assertEqual(errors, []) def test_missing_sections_reports_each_heading(self) -> None: missing = missing_sections("# 页面\n## 已有\n", ("## 已有", "## 缺少")) self.assertEqual(missing, ["## 缺少"]) def test_every_core_document_is_required(self) -> None: for path in CORE_DOCUMENT_REQUIREMENTS: self.assertIn(path, REQUIRED_FILES) def test_every_core_page_has_exact_mapping(self) -> None: config = load_config() mappings = {mapping.page: mapping.path for mapping in config.mappings} for page, path in CORE_PAGE_PATHS.items(): self.assertEqual(mappings.get(page), path) def test_task_archives_are_not_core_mappings(self) -> None: config = load_config() self.assertFalse( any(mapping.path.startswith("docs/task/") for mapping in config.mappings) ) def test_existing_project_adoption_guide_is_core_document(self) -> None: path = "docs/08-existing-project-adoption.md" self.assertEqual( CORE_PAGE_PATHS.get("Existing-Project-Adoption-Guide"), path, ) self.assertIn(path, CORE_DOCUMENT_REQUIREMENTS) self.assertIn("## 可复制 Agent 指令", CORE_DOCUMENT_REQUIREMENTS[path]) def test_new_project_setup_requires_baseline_selection(self) -> None: required = CORE_DOCUMENT_REQUIREMENTS[ "docs/07-new-project-documentation-setup.md" ] self.assertIn("### 2. 选择建设基线", required) self.assertIn("#### 判断案例", required) self.assertIn("### 3. 识别子项目与交付单元", required) def test_missing_or_wrong_core_mapping_is_reported(self) -> None: errors = core_mapping_errors({"Home": "docs/wrong.md"}) self.assertTrue(any("Home -> docs/README.md" in error for error in errors)) self.assertTrue( any("Architecture-and-Code-Map" in error for error in errors) ) class TaskTemplateTests(unittest.TestCase): def test_task_template_requires_document_impact(self) -> None: errors: list[str] = [] check_task_template(errors) self.assertEqual(errors, []) def test_task_template_requires_delivery_document_impact(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) template = root / ".gitea" / "issue_template" / "task.md" template.parent.mkdir(parents=True) template.write_text( "## 依赖与并行\n" "- 前置工单:无 / #编号\n" "- 是否允许与前置工单并行:是 / 否\n" "- 原因:\n" "## 原始需求\n" "- 来源:用户对话 / Gitea / 其他\n" "- 提出时间:\n" "- 关键原话或脱敏摘要:\n" "## 需求变化记录\n" "| 日期 | 变化内容 | 原因 | 用户确认 |\n" "## 文档影响\n" "- [ ] 不影响长期文档,原因:\n" "- [ ] 更新架构与代码地图\n" "- [ ] 更新业务规则与术语\n" "- [ ] 更新常见修改或故障排查\n", encoding="utf-8", ) errors: list[str] = [] check_task_template(errors, root) self.assertIn("单元任务模板缺少:## 交付文档影响", errors) self.assertIn( "单元任务模板缺少:- [ ] 无交付文档影响,原因:", errors, ) def test_task_template_requires_subproject_impact(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) template = root / ".gitea" / "issue_template" / "task.md" template.parent.mkdir(parents=True) template.write_text("## 基本信息\n", encoding="utf-8") errors: list[str] = [] check_task_template(errors, root) self.assertIn("单元任务模板缺少:## 子项目影响", errors) self.assertIn( "单元任务模板缺少:- 是否跨子项目:是 / 否", errors, ) self.assertIn( "单元任务模板缺少:- 是否修改共享接口或契约:是 / 否;唯一事实来源:", errors, ) def test_task_template_requires_dependency_fields(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) template = root / ".gitea" / "issue_template" / "task.md" template.parent.mkdir(parents=True) template.write_text( "## 文档影响\n" "- [ ] 不影响长期文档,原因:\n" "- [ ] 更新架构与代码地图\n" "- [ ] 更新业务规则与术语\n" "- [ ] 更新常见修改或故障排查\n", encoding="utf-8", ) errors: list[str] = [] check_task_template(errors, root) self.assertIn("单元任务模板缺少:## 依赖与并行", errors) self.assertIn("单元任务模板缺少:- 前置工单:无 / #编号", errors) def test_task_template_requires_requirement_traceability(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) template = root / ".gitea" / "issue_template" / "task.md" template.parent.mkdir(parents=True) template.write_text( "## 依赖与并行\n" "- 前置工单:无 / #编号\n" "- 是否允许与前置工单并行:是 / 否\n" "- 原因:\n" "## 文档影响\n" "- [ ] 不影响长期文档,原因:\n" "- [ ] 更新架构与代码地图\n" "- [ ] 更新业务规则与术语\n" "- [ ] 更新常见修改或故障排查\n", encoding="utf-8", ) errors: list[str] = [] check_task_template(errors, root) self.assertIn("单元任务模板缺少:## 原始需求", errors) self.assertIn("单元任务模板缺少:## 需求变化记录", errors) class AgentRuleTests(unittest.TestCase): def test_required_agent_rules_are_present(self) -> None: errors: list[str] = [] check_agent_efficiency_rules(errors) self.assertEqual(errors, []) def test_claude_code_entry_imports_shared_rules(self) -> None: errors: list[str] = [] check_claude_code_entry(errors) self.assertEqual(errors, []) def test_claude_code_entry_requires_exact_import_line(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) (root / "CLAUDE.md").write_text( "共同规则事实来源\n只记录 Claude Code 特有\n" "共同规则只修改 `AGENTS.md`\n", encoding="utf-8", ) errors: list[str] = [] check_claude_code_entry(errors, root) self.assertIn("CLAUDE.md 缺少独立的 @AGENTS.md 导入", errors) if __name__ == "__main__": unittest.main()