blob: 5b367232a90f9bcc99d5d1a26b35cd719645f479 [file] [edit]
import json
import os
import subprocess
from pathlib import Path
import yaml
PROJECT_ROOT = Path(__file__).parents[2]
def _load_json(relative_path: str) -> dict:
path = PROJECT_ROOT / relative_path
return json.loads(path.read_text())
def _skill_metadata(path: Path) -> dict[str, str]:
content = path.read_text()
assert content.startswith("---\n")
_, front_matter, _ = content.split("---", 2)
metadata = {}
for line in front_matter.splitlines():
if ":" in line:
key, value = line.split(":", 1)
metadata[key] = value.strip().strip('"')
return metadata
def _run_config_hook(
tmp_path: Path,
return_code: int,
*,
codex_environment: bool = False,
) -> subprocess.CompletedProcess:
fake_bin = tmp_path / "bin"
fake_bin.mkdir()
args_path = tmp_path / "uv-args"
environment_path = tmp_path / "uv-environment"
fake_uv = fake_bin / "uv"
fake_uv.write_text(
"#!/usr/bin/env bash\n"
'printf \'%s\\n\' "$*" > "$FAKE_UV_ARGS_PATH"\n'
'printf \'%s\\n\' "$UV_PROJECT_ENVIRONMENT" > "$FAKE_UV_ENVIRONMENT_PATH"\n'
"printf '%s\\n' \"$FAKE_UV_OUTPUT\"\n"
'exit "$FAKE_UV_RETURN_CODE"\n'
)
fake_uv.chmod(0o755)
plugin_data = tmp_path / "plugin-data"
environment = os.environ.copy()
environment.pop("CLAUDE_PLUGIN_ROOT", None)
environment.pop("CLAUDE_PLUGIN_DATA", None)
environment.pop("PLUGIN_ROOT", None)
environment.pop("PLUGIN_DATA", None)
environment.update(
{
"PLUGIN_ROOT" if codex_environment else "CLAUDE_PLUGIN_ROOT": str(
PROJECT_ROOT
),
"PLUGIN_DATA" if codex_environment else "CLAUDE_PLUGIN_DATA": str(
plugin_data
),
"FAKE_UV_ARGS_PATH": str(args_path),
"FAKE_UV_ENVIRONMENT_PATH": str(environment_path),
"FAKE_UV_OUTPUT": "controlled uv output",
"FAKE_UV_RETURN_CODE": str(return_code),
"PATH": f"{fake_bin}{os.pathsep}{environment['PATH']}",
}
)
return subprocess.run(
["bash", str(PROJECT_ROOT / "hooks/check-config.sh")],
cwd=PROJECT_ROOT,
env=environment,
capture_output=True,
text=True,
check=False,
)
def test_claude_plugin_manifest_identifies_gerrit():
manifest = _load_json(".claude-plugin/plugin.json")
assert manifest["name"] == "gerrit"
assert manifest["license"] == "Apache-2.0"
assert manifest["repository"] == "https://gerrit.googlesource.com/gerrit-mcp-server"
def test_claude_marketplace_installs_plugin_from_repository_root():
marketplace = _load_json(".claude-plugin/marketplace.json")
assert marketplace["name"] == "gerrit-mcp"
assert marketplace["plugins"] == [{"name": "gerrit", "source": "./"}]
def test_claude_mcp_manifest_uses_plugin_data_for_environment():
server = _load_json(".mcp.json")["mcpServers"]["gerrit"]
assert server["command"] == "uv"
assert server["args"] == [
"run",
"--directory",
"${CLAUDE_PLUGIN_ROOT}",
"gerrit-mcp-server",
]
assert server["env"] == {"UV_PROJECT_ENVIRONMENT": "${CLAUDE_PLUGIN_DATA}/.venv"}
def test_claude_skills_have_required_agent_skill_metadata():
skill_paths = sorted((PROJECT_ROOT / "skills").glob("*/SKILL.md"))
assert skill_paths
for skill_path in skill_paths:
metadata = _skill_metadata(skill_path)
assert metadata["name"]
assert metadata["description"]
def test_config_hook_reports_valid_configuration(tmp_path):
result = _run_config_hook(tmp_path, return_code=0)
assert result.returncode == 0
payload = json.loads(result.stdout)
assert "systemMessage" not in payload
assert "tools are available" in payload["hookSpecificOutput"]["additionalContext"]
def test_config_hook_reports_missing_configuration(tmp_path):
result = _run_config_hook(tmp_path, return_code=1)
assert result.returncode == 0
payload = json.loads(result.stdout)
assert "no config yet" in payload["systemMessage"]
assert "/gerrit:setup" in payload["hookSpecificOutput"]["additionalContext"]
def test_config_hook_reports_invalid_configuration(tmp_path):
result = _run_config_hook(tmp_path, return_code=2)
assert result.returncode == 0
payload = json.loads(result.stdout)
assert "config is invalid" in payload["systemMessage"]
assert "controlled uv output" in payload["hookSpecificOutput"]["additionalContext"]
def test_config_hook_passes_plugin_paths_to_uv(tmp_path):
result = _run_config_hook(tmp_path, return_code=0)
assert result.returncode == 0
assert (tmp_path / "uv-args").read_text().strip() == (
f"run --directory {PROJECT_ROOT} gerrit-check-config"
)
assert (tmp_path / "uv-environment").read_text().strip() == (
f"{tmp_path / 'plugin-data'}/.venv"
)
def test_codex_plugin_manifest_identifies_gerrit():
manifest = _load_json("plugin.json")
assert manifest["$schema"] == (
"https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"
)
assert manifest["name"] == "gerrit"
assert manifest["version"]
assert manifest["license"] == "Apache-2.0"
assert manifest["extensions"]["com.openai"]["hooks"] == ("./hooks/hooks.json")
def test_codex_mcp_manifest_uses_codex_plugin_runtime():
server = _load_json("mcp.json")["mcpServers"]["gerrit"]
assert server["type"] == "stdio"
assert server["command"] == "uv"
assert server["args"] == [
"run",
"--directory",
"${PLUGIN_ROOT}",
"gerrit-mcp-server",
]
assert server["env"] == {
"UV_PROJECT_ENVIRONMENT": "${PLUGIN_DATA}/.venv",
}
def test_codex_setup_skill_disables_implicit_invocation():
metadata = yaml.safe_load(
(PROJECT_ROOT / "skills/setup/agents/openai.yaml").read_text()
)
assert metadata["policy"]["allow_implicit_invocation"] is False
def test_codex_mcp_manifest_forwards_config_and_extension_environment():
server = _load_json(".codex-plugin/plugin.json")["mcpServers"]["gerrit"]
assert server.get("env_vars") == [
"GERRIT_CONFIG_PATH",
"XDG_CONFIG_HOME",
"GERRIT_MCP_EXTENSIONS",
]
def test_config_hook_accepts_codex_plugin_paths(tmp_path):
result = _run_config_hook(tmp_path, return_code=0, codex_environment=True)
assert result.returncode == 0
assert (tmp_path / "uv-args").read_text().strip() == (
f"run --directory {PROJECT_ROOT} gerrit-check-config"
)
assert (tmp_path / "uv-environment").read_text().strip() == (
f"{tmp_path / 'plugin-data'}/.venv"
)