91 lines
3.6 KiB
Python
91 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Skill 目录命名与 frontmatter 一致性的离线测试。"""
|
|
|
|
import pathlib
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
PROJECT_ROOT = pathlib.Path(__file__).resolve().parents[3]
|
|
SCRIPTS_DIR = PROJECT_ROOT / "muse" / "authority" / "evidence" / "skills" / "access-database" / "scripts"
|
|
sys.path.insert(0, str(SCRIPTS_DIR))
|
|
|
|
from muse_role_contract import load_role_contract_catalog
|
|
from sync_agent_registry import validate_role_catalog, validate_skill_catalog
|
|
|
|
|
|
class SkillCatalogTest(unittest.TestCase):
|
|
def test_current_role_catalog_is_exact_and_host_neutral(self):
|
|
entries = validate_role_catalog()
|
|
self.assertEqual(
|
|
{fm["name"] for _, fm in entries},
|
|
{"writer", "planner", "extractor", "detector", "judge"},
|
|
)
|
|
self.assertTrue(all("model" not in fm for _, fm in entries))
|
|
|
|
def test_role_contract_is_central_and_complete(self):
|
|
catalog = load_role_contract_catalog(PROJECT_ROOT)
|
|
self.assertEqual(
|
|
set(catalog.roles),
|
|
{"writer", "planner", "extractor", "detector", "judge"},
|
|
)
|
|
self.assertTrue(all(role.contract_prompt for role in catalog.roles.values()))
|
|
self.assertTrue(all(role.explicit_model_required for role in catalog.roles.values()))
|
|
|
|
def test_role_frontmatter_rejects_static_model_binding(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
for role in ("writer", "planner", "extractor", "detector", "judge"):
|
|
extra = "model: opus\n" if role == "writer" else ""
|
|
(root / f"{role}.md").write_text(
|
|
f"---\nname: {role}\ndescription: test\nskills: test-skill\ntools: read\n{extra}---\n",
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "未登记字段"):
|
|
validate_role_catalog(root)
|
|
|
|
def test_current_catalog_is_valid(self):
|
|
entries = validate_skill_catalog()
|
|
self.assertGreater(len(entries), 0)
|
|
self.assertEqual(len(entries), len({fm["name"] for _, fm in entries}))
|
|
|
|
def test_directory_and_name_must_match(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
skill = root / "plan-story"
|
|
skill.mkdir()
|
|
(skill / "SKILL.md").write_text(
|
|
"---\nname: planning\ndescription: test\n---\n",
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "目录名"):
|
|
validate_skill_catalog(root)
|
|
|
|
def test_name_must_be_action_object(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
skill = root / "runtime"
|
|
skill.mkdir()
|
|
(skill / "SKILL.md").write_text(
|
|
"---\nname: runtime\ndescription: test\n---\n",
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "动作-对象"):
|
|
validate_skill_catalog(root)
|
|
|
|
def test_unknown_frontmatter_key_is_rejected(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
root = pathlib.Path(tmp)
|
|
skill = root / "plan-story"
|
|
skill.mkdir()
|
|
(skill / "SKILL.md").write_text(
|
|
"---\nname: plan-story\ndescription: test\nunknown: value\n---\n",
|
|
encoding="utf-8",
|
|
)
|
|
with self.assertRaisesRegex(ValueError, "未登记字段"):
|
|
validate_skill_catalog(root)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|