127 lines
5.2 KiB
Python
127 lines
5.2 KiB
Python
import importlib.util
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SKILL_LIB_ROOT = REPO_ROOT.parent / "skill_lib"
|
|
SKILLS_DIR = SKILL_LIB_ROOT / "skills"
|
|
VALIDATOR_PATH = REPO_ROOT / "scripts" / "validate_skill_lib.py"
|
|
EXPECTED_SKILL_NAMES = [
|
|
"office-export-xlsx",
|
|
"zhihu-hotlist",
|
|
"zhihu-hotlist-screen",
|
|
"zhihu-navigate",
|
|
"zhihu-write",
|
|
]
|
|
|
|
|
|
def load_validator_module():
|
|
spec = importlib.util.spec_from_file_location("validate_skill_lib", VALIDATOR_PATH)
|
|
module = importlib.util.module_from_spec(spec)
|
|
assert spec is not None
|
|
assert spec.loader is not None
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class SkillLibValidationTest(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.validator = load_validator_module()
|
|
|
|
def test_discovers_expected_skill_packages(self):
|
|
skill_dirs = self.validator.discover_skill_dirs()
|
|
self.assertEqual([path.name for path in skill_dirs], EXPECTED_SKILL_NAMES)
|
|
|
|
def test_load_skill_matches_current_metadata(self):
|
|
loaded = {}
|
|
for skill_dir in self.validator.discover_skill_dirs():
|
|
record = self.validator.load_skill(skill_dir)
|
|
loaded[record.name] = record
|
|
|
|
self.assertEqual(sorted(loaded), EXPECTED_SKILL_NAMES)
|
|
|
|
for name, record in loaded.items():
|
|
self.assertEqual(record.name, name)
|
|
self.assertEqual(record.version, "0.1.0")
|
|
self.assertEqual(record.author, "sgclaw")
|
|
self.assertTrue(record.description.startswith("Use when"))
|
|
if name.startswith("zhihu-"):
|
|
self.assertIn("zhihu", record.tags)
|
|
self.assertIn("browser", record.tags)
|
|
if name == "office-export-xlsx":
|
|
self.assertIn("office", record.tags)
|
|
self.assertIn("xlsx", record.tags)
|
|
expected_location = (
|
|
SKILLS_DIR / name / "SKILL.toml"
|
|
if name == "zhihu-hotlist"
|
|
else SKILLS_DIR / name / "SKILL.md"
|
|
)
|
|
self.assertEqual(record.location, expected_location)
|
|
self.assertTrue(record.prompt_body.lstrip().startswith("# "))
|
|
self.assertNotIn("\n---\n", record.prompt_body)
|
|
|
|
def test_each_skill_passes_audit_with_current_script_policy(self):
|
|
for skill_dir in self.validator.discover_skill_dirs():
|
|
report = self.validator.audit_skill_directory(skill_dir, allow_scripts=True)
|
|
self.assertEqual(
|
|
report.findings,
|
|
[],
|
|
f"{skill_dir.name} findings: {report.findings}",
|
|
)
|
|
|
|
def test_current_packages_keep_required_structure(self):
|
|
for name in EXPECTED_SKILL_NAMES:
|
|
skill_dir = SKILLS_DIR / name
|
|
self.assertTrue(
|
|
(skill_dir / "SKILL.md").is_file() or (skill_dir / "SKILL.toml").is_file()
|
|
)
|
|
self.assertTrue((skill_dir / "references").is_dir())
|
|
self.assertTrue((skill_dir / "assets").is_dir())
|
|
self.assertTrue((SKILLS_DIR / "zhihu-hotlist" / "SKILL.toml").is_file())
|
|
self.assertTrue(
|
|
(SKILLS_DIR / "zhihu-hotlist" / "scripts" / "extract_hotlist.js").is_file()
|
|
)
|
|
|
|
def test_each_skill_declares_superrpa_browser_contract(self):
|
|
for name in [name for name in EXPECTED_SKILL_NAMES if name.startswith("zhihu-")]:
|
|
content = (SKILLS_DIR / name / "SKILL.md").read_text(encoding="utf-8")
|
|
self.assertIn("superrpa_browser", content)
|
|
self.assertIn("expected_domain", content)
|
|
self.assertIn("CSS", content)
|
|
|
|
def test_zhihu_hotlist_declares_export_artifact_contract(self):
|
|
content = (SKILLS_DIR / "zhihu-hotlist" / "SKILL.md").read_text(encoding="utf-8")
|
|
self.assertIn("Export Artifact", content)
|
|
self.assertIn('"sheet_name": "知乎热榜"', content)
|
|
self.assertIn('"columns": ["rank", "title", "heat"]', content)
|
|
self.assertIn('"rows": [[1, "标题", "344万"]]', content)
|
|
self.assertIn("structured artifact is primary", content)
|
|
|
|
def test_office_export_skill_declares_openxml_contract(self):
|
|
content = (SKILLS_DIR / "office-export-xlsx" / "SKILL.md").read_text(encoding="utf-8")
|
|
self.assertIn("openxml_office", content)
|
|
self.assertIn(".xlsx", content)
|
|
self.assertIn("sheet_name", content)
|
|
self.assertIn("columns", content)
|
|
self.assertIn("rows", content)
|
|
|
|
def test_hotlist_screen_skill_declares_echarts_html_contract(self):
|
|
content = (SKILLS_DIR / "zhihu-hotlist-screen" / "SKILL.md").read_text(encoding="utf-8")
|
|
self.assertIn("screen_html_export", content)
|
|
self.assertIn(".html", content)
|
|
self.assertIn("ECharts", content)
|
|
self.assertIn("大屏", content)
|
|
self.assertIn("新标签页", content)
|
|
self.assertIn("presentation", content)
|
|
|
|
def test_validate_all_skills_reports_pass(self):
|
|
results = self.validator.validate_all_skills(allow_scripts=True)
|
|
self.assertEqual([result.record.name for result in results], EXPECTED_SKILL_NAMES)
|
|
self.assertTrue(all(result.ok for result in results))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|