Files
yovision/tests/test_harness_docs.py

262 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_go_admin_ui_rules,
check_goadmin_baseline,
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_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_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_coordination_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", encoding="utf-8")
errors: list[str] = []
check_task_template(errors, root)
self.assertIn(
"单元任务模板缺少:- 任务类型:单项目 / 协同",
errors,
)
self.assertIn("单元任务模板缺少:- 主 agent:", errors)
self.assertIn("单元任务模板缺少:## 协同接口", errors)
self.assertIn("单元任务模板缺少:- 生产者:", errors)
self.assertIn("单元任务模板缺少:- 消费者:", errors)
self.assertIn("单元任务模板缺少:- write_paths:", 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_go_admin_ui_rules_are_present(self) -> None:
errors: list[str] = []
check_go_admin_ui_rules(errors)
self.assertEqual(errors, [])
def test_go_admin_ui_rules_require_each_scope(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "Sense").mkdir()
(root / "Bell").mkdir()
(root / "AGENTS.md").write_text("", encoding="utf-8")
(root / "Sense" / "AGENTS.md").write_text("", encoding="utf-8")
(root / "Bell" / "AGENTS.md").write_text("", encoding="utf-8")
errors: list[str] = []
check_go_admin_ui_rules(errors, root)
self.assertTrue(any(error.startswith("AGENTS.md ") for error in errors))
self.assertTrue(any(error.startswith("Sense/AGENTS.md ") for error in errors))
self.assertTrue(any(error.startswith("Bell/AGENTS.md ") for error in errors))
def test_go_admin_ui_rules_report_missing_scope_file(self) -> None:
with tempfile.TemporaryDirectory() as directory:
errors: list[str] = []
check_go_admin_ui_rules(errors, Path(directory))
self.assertIn("缺少 go-admin 规则文件:AGENTS.md", errors)
self.assertIn("缺少 go-admin 规则文件:Sense/AGENTS.md", errors)
self.assertIn("缺少 go-admin 规则文件:Bell/AGENTS.md", errors)
def test_goadmin_baseline_is_frozen(self) -> None:
errors: list[str] = []
check_goadmin_baseline(errors)
self.assertEqual(errors, [])
def test_goadmin_baseline_rejects_unpinned_source(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "goadmin-baseline.json").write_text(
'{"authority":"tag","runtimes":{},'
'"sources":{"go-admin":{}},"rules":{}}',
encoding="utf-8",
)
errors: list[str] = []
check_goadmin_baseline(errors, root)
self.assertTrue(any("go-admin.commit" in error for error in errors))
self.assertIn(
"GoAdmin 技术基线必须以上游 URL 和 commit 为权威标识",
errors,
)
def test_goadmin_baseline_reports_missing_file(self) -> None:
with tempfile.TemporaryDirectory() as directory:
errors: list[str] = []
check_goadmin_baseline(errors, Path(directory))
self.assertEqual(
errors,
["缺少 GoAdmin 技术基线:goadmin-baseline.json"],
)
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()