Files
antigravity-skills-reference/tools/scripts/tests/test_editorial_bundles.py

216 lines
9.0 KiB
Python

import importlib.util
import errno
import pathlib
import sys
import tempfile
from unittest import mock
import unittest
REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
TOOLS_SCRIPTS = REPO_ROOT / "tools" / "scripts"
def load_module(module_path: pathlib.Path, module_name: str):
sys.path.insert(0, str(module_path.parent))
spec = importlib.util.spec_from_file_location(module_name, module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
editorial_bundles = load_module(
TOOLS_SCRIPTS / "sync_editorial_bundles.py",
"sync_editorial_bundles",
)
plugin_compatibility = load_module(
TOOLS_SCRIPTS / "plugin_compatibility.py",
"plugin_compatibility_json",
)
get_bundle_skills = load_module(
TOOLS_SCRIPTS / "get-bundle-skills.py",
"get_bundle_skills_json",
)
class EditorialBundlesTests(unittest.TestCase):
def setUp(self):
self.manifest_bundles = editorial_bundles.load_editorial_bundles(REPO_ROOT)
self.compatibility_report = plugin_compatibility.load_plugin_compatibility(REPO_ROOT)
self.compatibility_by_id = plugin_compatibility.compatibility_by_skill_id(self.compatibility_report)
def test_manifest_has_unique_ids_and_existing_skills(self):
bundle_ids = [bundle["id"] for bundle in self.manifest_bundles]
self.assertEqual(len(bundle_ids), len(set(bundle_ids)))
for bundle in self.manifest_bundles:
self.assertEqual(bundle["id"], get_bundle_skills._normalize_bundle_query(bundle["name"]))
self.assertTrue(bundle["skills"], f'bundle "{bundle["id"]}" should not be empty')
for skill in bundle["skills"]:
self.assertTrue((REPO_ROOT / "skills" / skill["id"]).exists())
def test_bundles_doc_matches_renderer(self):
metadata = editorial_bundles.load_metadata(str(REPO_ROOT))
expected = editorial_bundles.render_bundles_doc(
REPO_ROOT,
metadata,
self.manifest_bundles,
self.compatibility_by_id,
)
actual = (REPO_ROOT / "docs" / "users" / "bundles.md").read_text(encoding="utf-8")
self.assertEqual(actual, expected)
def test_get_bundle_skills_reads_json_manifest_by_name_and_id(self):
expected = ["concise-planning", "git-pushing", "kaizen", "lint-and-validate", "systematic-debugging"]
self.assertEqual(get_bundle_skills.get_bundle_skills(["Essentials"]), expected)
self.assertEqual(get_bundle_skills.get_bundle_skills(["essentials"]), expected)
web_wizard_skills = get_bundle_skills.get_bundle_skills(["web-wizard"])
self.assertIn("form-cro", web_wizard_skills)
self.assertIn("react-best-practices", web_wizard_skills)
self.assertIn(
"game-development/game-design",
get_bundle_skills.get_bundle_skills(["indie-game-dev"]),
)
def test_generated_bundle_plugin_contains_expected_skills(self):
essentials_plugin = REPO_ROOT / "plugins" / "antigravity-bundle-essentials" / "skills"
expected_ids = {
skill["id"]
for skill in next(bundle for bundle in self.manifest_bundles if bundle["id"] == "essentials")["skills"]
}
actual_ids = {
str(path.relative_to(essentials_plugin))
for path in essentials_plugin.rglob("SKILL.md")
}
self.assertEqual(actual_ids, {f"{skill_id}/SKILL.md" for skill_id in expected_ids})
sample_skill_dir = essentials_plugin / "concise-planning"
self.assertTrue((sample_skill_dir / "SKILL.md").is_file())
def test_generated_plugin_count_matches_manifest(self):
generated_plugins = sorted(
path.name
for path in (REPO_ROOT / "plugins").iterdir()
if path.is_dir() and path.name.startswith("antigravity-bundle-")
)
expected_plugins = sorted(
f'antigravity-bundle-{bundle["id"]}'
for bundle in self.manifest_bundles
if any(
all(
self.compatibility_by_id[skill["id"]]["targets"][target] == "supported"
for skill in bundle["skills"]
)
for target in ("codex", "claude")
)
)
self.assertEqual(generated_plugins, expected_plugins)
def test_manifest_rejects_bundle_ids_with_path_traversal(self):
with tempfile.TemporaryDirectory() as temp_dir:
temp_root = pathlib.Path(temp_dir)
skill_dir = temp_root / "skills" / "safe-skill"
skill_dir.mkdir(parents=True, exist_ok=True)
payload = {
"bundles": [
{
"id": "../../outside",
"name": "Safe Bundle",
"group": "Security",
"emoji": "🛡️",
"tagline": "Test bundle",
"audience": "Testers",
"description": "Testers",
"skills": [{"id": "safe-skill", "summary": "ok"}],
}
]
}
with self.assertRaisesRegex(ValueError, "Invalid editorial bundle id"):
editorial_bundles._validate_editorial_bundles(temp_root, payload)
def test_sample_bundle_copy_matches_source_file_inventory(self):
sample_bundle = next(bundle for bundle in self.manifest_bundles if bundle["id"] == "documents-presentations")
plugin_skills_root = REPO_ROOT / "plugins" / "antigravity-bundle-documents-presentations" / "skills"
for skill in sample_bundle["skills"]:
source_dir = REPO_ROOT / "skills" / skill["id"]
copied_dir = plugin_skills_root / skill["id"]
self.assertTrue(copied_dir.is_dir(), f'copied skill dir missing for {skill["id"]}')
source_files = sorted(
str(path.relative_to(source_dir))
for path in source_dir.rglob("*")
if path.is_file()
)
copied_files = sorted(
str(path.relative_to(copied_dir))
for path in copied_dir.rglob("*")
if path.is_file()
)
self.assertEqual(copied_files, source_files, f'copied bundle skill should match source inventory for {skill["id"]}')
def test_root_plugins_only_include_supported_skills_for_target(self):
codex_root = REPO_ROOT / "plugins" / "antigravity-awesome-skills" / "skills"
claude_root = REPO_ROOT / "plugins" / "antigravity-awesome-skills-claude" / "skills"
for skill_id, compatibility in self.compatibility_by_id.items():
codex_path = codex_root / skill_id
claude_path = claude_root / skill_id
self.assertEqual(
codex_path.exists(),
compatibility["targets"]["codex"] == "supported",
f"Codex root plugin inclusion mismatch for {skill_id}",
)
self.assertEqual(
claude_path.exists(),
compatibility["targets"]["claude"] == "supported",
f"Claude root plugin inclusion mismatch for {skill_id}",
)
def test_remove_tree_retries_on_enotempty(self):
target = REPO_ROOT / "plugins" / "antigravity-awesome-skills" / "skills"
calls = {"count": 0}
def flaky_rmtree(path):
calls["count"] += 1
if calls["count"] == 1:
raise OSError(errno.ENOTEMPTY, "Directory not empty")
with mock.patch.object(editorial_bundles.shutil, "rmtree", side_effect=flaky_rmtree):
with mock.patch.object(editorial_bundles.time, "sleep") as sleep_mock:
editorial_bundles._remove_tree(target)
self.assertEqual(calls["count"], 2)
sleep_mock.assert_called_once()
def test_replace_directory_atomically_swaps_only_after_staging_is_ready(self):
with tempfile.TemporaryDirectory() as temp_dir:
temp_root = pathlib.Path(temp_dir)
destination = temp_root / "plugin"
old_file = destination / "skills" / "old.txt"
old_file.parent.mkdir(parents=True, exist_ok=True)
old_file.write_text("old", encoding="utf-8")
observed = {}
def populate(staging_root):
new_file = staging_root / "skills" / "new.txt"
new_file.parent.mkdir(parents=True, exist_ok=True)
new_file.write_text("new", encoding="utf-8")
observed["old_visible_during_populate"] = old_file.is_file()
observed["new_hidden_during_populate"] = not (destination / "skills" / "new.txt").exists()
editorial_bundles._replace_directory_atomically(destination, populate)
self.assertTrue(observed["old_visible_during_populate"])
self.assertTrue(observed["new_hidden_during_populate"])
self.assertFalse(old_file.exists())
self.assertTrue((destination / "skills" / "new.txt").is_file())
if __name__ == "__main__":
unittest.main()