211 lines
8.4 KiB
Python
211 lines
8.4 KiB
Python
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_existing_project_adoption_requires_upgrade_process(self) -> None:
|
|
required = CORE_DOCUMENT_REQUIREMENTS[
|
|
"docs/08-existing-project-adoption.md"
|
|
]
|
|
self.assertIn("## 后续升级", required)
|
|
self.assertIn("### 升级步骤", required)
|
|
self.assertIn("### 可复制升级指令", required)
|
|
|
|
def test_project_profile_requires_dev_harness_baseline(self) -> None:
|
|
required = CORE_DOCUMENT_REQUIREMENTS["docs/00-project-profile.md"]
|
|
self.assertIn("## DevHarness 来源与基线", required)
|
|
|
|
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()
|