feat(skill): 新增项目规范与初始化 Skill
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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(
|
||||
".craftkit/standards/index.md",
|
||||
payload["guidance"]["standardsIndex"],
|
||||
)
|
||||
serialized = json.dumps(payload, ensure_ascii=False)
|
||||
self.assertNotIn(":\\", serialized)
|
||||
|
||||
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()
|
||||
Reference in New Issue
Block a user