mirror of
https://github.com/vintasoftware/django-ai-plugins.git
synced 2026-09-14 16:38:37 +08:00
96 lines
3.6 KiB
Python
96 lines
3.6 KiB
Python
import json
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
class DocumentationContractTests(unittest.TestCase):
|
|
def load_catalog(self):
|
|
return json.loads((ROOT / "plugins" / "catalog.json").read_text())
|
|
|
|
def test_root_readme_links_shared_installation_and_compatibility_docs(self):
|
|
readme = (ROOT / "README.md").read_text()
|
|
|
|
self.assertIn("docs/installation.md", readme)
|
|
self.assertIn("docs/compatibility.md", readme)
|
|
for plugin in self.load_catalog()["plugins"]:
|
|
self.assertIn(plugin["id"], readme)
|
|
|
|
def test_installation_doc_covers_every_host_and_lifecycle_operation(self):
|
|
installation = (ROOT / "docs" / "installation.md").read_text().lower()
|
|
|
|
for host in ("claude", "codex", "cursor", "opencode", "agent skills"):
|
|
self.assertIn(host, installation)
|
|
for operation in (
|
|
"install",
|
|
"update",
|
|
"uninstall",
|
|
"cache",
|
|
"duplicate",
|
|
"permission",
|
|
):
|
|
self.assertIn(operation, installation)
|
|
|
|
def test_installation_doc_covers_skills_sh_lifecycle(self):
|
|
installation = (ROOT / "docs" / "installation.md").read_text()
|
|
|
|
self.assertIn(
|
|
"https://skills.sh/vintasoftware/django-ai-plugins",
|
|
installation,
|
|
)
|
|
for command in (
|
|
"npx skills add vintasoftware/django-ai-plugins --list",
|
|
"npx skills add vintasoftware/django-ai-plugins --skill django-expert",
|
|
"npx skills update django-expert",
|
|
"npx skills remove django-expert",
|
|
):
|
|
self.assertIn(command, installation)
|
|
self.assertIn("Do not install the same skill ID", installation)
|
|
|
|
def test_documented_local_commands_reference_existing_scripts(self):
|
|
installation = (ROOT / "docs" / "installation.md").read_text()
|
|
contributor = (ROOT / "AGENTS.md").read_text()
|
|
|
|
for script in (
|
|
"scripts/generate_adapters.py",
|
|
"scripts/validate_plugins.py",
|
|
"scripts/smoke_plugins.py",
|
|
):
|
|
self.assertIn(script, installation + contributor)
|
|
self.assertTrue((ROOT / script).is_file())
|
|
|
|
def test_plugin_readmes_are_capability_focused_and_link_shared_lifecycle(self):
|
|
for plugin in self.load_catalog()["plugins"]:
|
|
with self.subTest(plugin=plugin["id"]):
|
|
readme = (ROOT / plugin["package"] / "README.md").read_text()
|
|
self.assertIn("../../docs/installation.md", readme)
|
|
self.assertNotIn("/plugin marketplace add", readme)
|
|
self.assertNotIn("codex plugin marketplace add", readme)
|
|
|
|
def test_ci_runs_every_offline_contract_without_publishing(self):
|
|
workflow = (ROOT / ".github" / "workflows" / "validate-plugins.yml").read_text()
|
|
|
|
for command in (
|
|
"python scripts/generate_adapters.py --check",
|
|
"python scripts/validate_plugins.py",
|
|
"python -m unittest discover",
|
|
):
|
|
self.assertIn(command, workflow)
|
|
for target in ("claude", "codex", "cursor", "opencode", "agent-skills"):
|
|
self.assertIn(f"--target {target}", workflow)
|
|
self.assertNotIn("publish", workflow.lower())
|
|
self.assertNotIn("secrets.", workflow)
|
|
|
|
def test_generated_outputs_are_declared_non_editable(self):
|
|
contributor = (ROOT / "AGENTS.md").read_text()
|
|
|
|
self.assertIn("Do not edit generated files directly", contributor)
|
|
self.assertIn("plugins/catalog.json", contributor)
|
|
self.assertIn("skills/<id>/", contributor)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|