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()