import json import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] MANIFEST = ROOT / ".gitea" / "ci" / "thirdparty_skills.json" WORKFLOW = ROOT / ".gitea" / "workflows" / "update-thirdparty-skills.yml" LEGACY_WORKFLOW = ROOT / ".gitea" / "workflows" / "update-thirdparty-superpowers.yml" UPDATE_SCRIPT = ROOT / ".gitea" / "ci" / "update_thirdparty_skills.sh" SYNC_SCRIPT = ROOT / ".gitea" / "ci" / "sync_thirdparty_skills.sh" SKILLS_MD = ROOT / "SKILLS.md" SUPERPOWERS_LIST = ROOT / "codex" / "skills" / ".sources" / "superpowers.list" UI_UX_PRO_MAX_LIST = ROOT / "codex" / "skills" / ".sources" / "ui-ux-pro-max.list" UI_UX_PRO_MAX_DIR = ROOT / "codex" / "skills" / "ui-ux-pro-max" def load_manifest() -> dict: return json.loads(MANIFEST.read_text(encoding="utf-8")) class ThirdpartySkillsPipelineTests(unittest.TestCase): def test_manifest_declares_superpowers_and_ui_ux_pro_max(self): data = load_manifest() self.assertEqual( [entry["id"] for entry in data["sources"]], ["superpowers", "ui-ux-pro-max"], ) def test_ui_ux_pro_max_uses_render_codex_skill_sync_mode(self): data = load_manifest() ui_skill = next(item for item in data["sources"] if item["id"] == "ui-ux-pro-max") self.assertEqual(ui_skill["sync_mode"], "render_codex_skill") self.assertEqual(ui_skill["snapshot_dir"], "ui-ux-pro-max") def test_superpowers_manifest_excludes_ui_ux_pro_max_from_copy_mode(self): data = load_manifest() superpowers = next(item for item in data["sources"] if item["id"] == "superpowers") self.assertEqual(superpowers["exclude_skill_dirs"], ["ui-ux-pro-max"]) def test_workflow_uses_generic_scripts_and_single_serial_job(self): text = WORKFLOW.read_text(encoding="utf-8") self.assertFalse(LEGACY_WORKFLOW.exists()) self.assertIn("update_and_sync:", text) self.assertNotIn("\n update:\n", text) self.assertNotIn("\n sync:\n", text) self.assertIn("bash .gitea/ci/update_thirdparty_skills.sh", text) self.assertIn("bash .gitea/ci/sync_thirdparty_skills.sh", text) self.assertNotIn("git merge", text) self.assertNotIn("git pull", text) def test_workflow_has_serial_concurrency_and_literal_generic_paths(self): text = WORKFLOW.read_text(encoding="utf-8") self.assertIn("concurrency:", text) self.assertIn("update-thirdparty-${{ github.repository }}", text) self.assertIn('MANIFEST_PATH: ".gitea/ci/thirdparty_skills.json"', text) self.assertIn('TARGET_BRANCH="$THIRDPARTY_BRANCH" bash .gitea/ci/update_thirdparty_skills.sh', text) self.assertIn('TARGET_BRANCH="main" \\', text) self.assertIn('MANIFEST_PATH="$MANIFEST_PATH" \\', text) def test_generic_scripts_exist_and_use_manifest(self): update_text = UPDATE_SCRIPT.read_text(encoding="utf-8") sync_text = SYNC_SCRIPT.read_text(encoding="utf-8") self.assertIn('MANIFEST_PATH="${MANIFEST_PATH:-.gitea/ci/thirdparty_skills.json}"', update_text) self.assertIn('MANIFEST_PATH="${MANIFEST_PATH:-.gitea/ci/thirdparty_skills.json}"', sync_text) self.assertIn('TARGET_BRANCH="${TARGET_BRANCH:-thirdparty/skill}"', update_text) self.assertIn('TARGET_BRANCH="${TARGET_BRANCH:-main}"', sync_text) self.assertIn(':package: deps(thirdparty): update snapshots', update_text) self.assertIn(':package: deps(skills): sync thirdparty skills', sync_text) def test_skills_doc_points_to_generic_thirdparty_sources(self): text = SKILLS_MD.read_text(encoding="utf-8") self.assertIn("## 9. Third-party Skills", text) self.assertIn("来源:`codex/skills/.sources/`(第三方来源清单目录)。", text) self.assertNotIn("Third-party Skills (superpowers)", text) def test_superpowers_and_ui_ux_pro_max_source_lists_exist(self): self.assertTrue(SUPERPOWERS_LIST.is_file()) self.assertTrue(UI_UX_PRO_MAX_LIST.is_file()) self.assertIn("using-superpowers", SUPERPOWERS_LIST.read_text(encoding="utf-8")) self.assertIn("ui-ux-pro-max", UI_UX_PRO_MAX_LIST.read_text(encoding="utf-8")) def test_ui_ux_pro_max_output_exists_with_data_and_scripts(self): self.assertTrue((UI_UX_PRO_MAX_DIR / "SKILL.md").is_file()) self.assertTrue((UI_UX_PRO_MAX_DIR / "data").is_dir()) self.assertTrue((UI_UX_PRO_MAX_DIR / "scripts").is_dir()) def test_update_script_materializes_manifest_before_target_checkout(self): text = UPDATE_SCRIPT.read_text(encoding="utf-8") self.assertIn('manifest_copy="$tmp_dir/thirdparty_skills.json"', text) self.assertIn('cp "$MANIFEST_PATH" "$manifest_copy"', text) self.assertIn('MANIFEST_PATH="$manifest_copy"', text) self.assertIn('if ! emit_sources_tsv > "$sources_file"; then', text) self.assertNotIn("done < <(emit_sources_tsv)", text) self.assertLess( text.index('cp "$MANIFEST_PATH" "$manifest_copy"'), text.index('git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH"'), ) def test_sync_script_uses_non_whitespace_separator_for_optional_fields(self): text = SYNC_SCRIPT.read_text(encoding="utf-8") self.assertIn('"\\x1f".join(', text) self.assertIn("while IFS=$'\\x1f' read -r", text) self.assertNotIn("while IFS=$'\\t' read -r", text) self.assertIn("exclude_skill_dirs", text) self.assertIn('if is_excluded_skill_dir "$name" "$exclude_skill_dirs"; then', text) if __name__ == "__main__": unittest.main()