113 lines
4.9 KiB
Python
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()
|