Files
CraftKit/migration/tests/test_guidance_init.py
T

113 lines
4.9 KiB
Python

import json
import unittest
from pathlib import Path
ROOT = Path(__file__).parents[2]
GUIDANCE = ROOT / "plugins" / "skill" / "skills" / "guidance"
INIT = ROOT / "plugins" / "knowledge" / "skills" / "init"
class GuidanceInitTest(unittest.TestCase):
"""验证规范检索和项目初始化之间的职责边界。"""
def test_guidance_is_read_only_and_uses_progressive_sources(self) -> None:
"""检索 Skill 应声明项目优先、渐进读取且不隐式初始化。"""
content = (GUIDANCE / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("以只读方式", content)
self.assertIn("不得先递归加载整个知识库", content)
self.assertIn("不得在检索过程中隐式初始化", content)
self.assertIn(".craftkit/project.json", content)
def test_public_baseline_routes_frontend_and_backend(self) -> None:
"""公共基线应提供前后端入口,并让索引引用实际规则文件。"""
root = GUIDANCE / "references" / "guidance"
index = (root / "index.md").read_text(encoding="utf-8")
self.assertIn("frontend/index.md", index)
self.assertIn("backend/index.md", index)
self.assertEqual(6, len(list((root / "frontend").glob("*.md"))))
self.assertEqual(6, len(list((root / "backend").glob("*.md"))))
sources = (root / "sources.md").read_text(encoding="utf-8")
for authority in ("RFC 9110", "WCAG 2.2", "OWASP", "MDN Fetch API"):
self.assertIn(authority, sources)
def test_framework_profiles_are_selected_by_project_version(self) -> None:
"""普通任务只能使用当前 profile,且不存在的 profile 不得被猜测。"""
routing = (GUIDANCE / "references" / "profile-routing.md").read_text(
encoding="utf-8"
)
self.assertIn("普通开发任务只加载当前 profile", routing)
self.assertIn("升级、迁移或版本比较", routing)
self.assertIn("不默认最新版本", routing)
self.assertIn("不创建空目录", routing)
init = (INIT / "SKILL.md").read_text(encoding="utf-8")
config = (INIT / "references" / "project-config.md").read_text(
encoding="utf-8"
)
self.assertIn("精确版本、公共 profile", init)
for field in ('"name"', '"version"', '"profile"'):
self.assertIn(field, config)
def test_init_supports_existing_and_new_projects(self) -> None:
"""初始化 Skill 应同时包含成熟项目与空项目流程。"""
content = (INIT / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("成熟项目", content)
self.assertIn("空项目", content)
self.assertIn("参考项目", content)
self.assertTrue((INIT / "references" / "existing.md").is_file())
self.assertTrue((INIT / "references" / "new.md").is_file())
def test_project_template_has_safe_shared_fields(self) -> None:
"""项目模板应包含检索入口,且不得预置内部标识或机器路径。"""
path = INIT / "assets" / "project.json"
payload = json.loads(path.read_text(encoding="utf-8"))
self.assertEqual(1, payload["schemaVersion"])
self.assertIn(payload["initialization"]["mode"], {"existing", "new"})
self.assertEqual([], payload["dependencies"]["internal"])
self.assertEqual([], payload["frontend"]["componentLibraries"])
self.assertEqual([], payload["frontend"]["componentRoots"])
self.assertEqual([], payload["frontend"]["documentation"])
self.assertEqual(
".craftkit/standards/index.md",
payload["guidance"]["standardsIndex"],
)
serialized = json.dumps(payload, ensure_ascii=False)
self.assertNotIn(":\\", serialized)
def test_init_collects_frontend_component_metadata(self) -> None:
"""初始化应记录组件库证据和入口,但不能默认选择组件库。"""
content = "\n".join(
path.read_text(encoding="utf-8")
for path in (
INIT / "SKILL.md",
INIT / "references" / "project-config.md",
INIT / "references" / "existing.md",
INIT / "references" / "new.md",
)
)
self.assertIn("componentLibraries", content)
self.assertIn("组件根", content)
self.assertIn("不默认选择流行方案", content)
self.assertTrue((INIT / "assets" / "frontend-components.md").is_file())
def test_local_directories_are_the_only_nested_ignores(self) -> None:
"""初始化模板只排除本地状态和缓存,不得排除共享资料。"""
rules = (INIT / "assets" / "craftkit.gitignore").read_text(
encoding="utf-8"
)
active = [line for line in rules.splitlines() if line and not line.startswith("#")]
self.assertEqual(["/local/", "/cache/"], active)
if __name__ == "__main__":
unittest.main()