#!/bin/bash # test-plugin-discovery.sh — Smoke test for auto-discovery chain # Validates that plugin.json, hooks.json, and script references are consistent # Usage: bash tests/test-plugin-discovery.sh set -euo pipefail PLUGIN_ROOT="$(cd "$(dirname "$0")/.." && pwd)" PASS=0 FAIL=0 pass() { echo -e "\033[0;32m ✓ $1\033[0m"; PASS=$((PASS + 1)); } fail() { echo -e "\033[0;31m ✗ $1\033[0m"; FAIL=$((FAIL + 1)); } echo "=== Plugin Discovery Smoke Test ===" echo "" # ------------------------------------------------------- # Check 1: plugin.json # ------------------------------------------------------- echo "--- Check 1: plugin.json ---" PLUGIN_JSON="$PLUGIN_ROOT/.claude-plugin/plugin.json" if [ -f "$PLUGIN_JSON" ]; then pass "plugin.json exists" else fail "plugin.json missing at .claude-plugin/plugin.json" fi if grep -q '"auto_discover": true' "$PLUGIN_JSON" 2>/dev/null || grep -q '"auto_discover":true' "$PLUGIN_JSON" 2>/dev/null; then pass "auto_discover: true" else fail "auto_discover is not true" fi # plugin.json must NOT have a "hooks" key if grep -q '"hooks"' "$PLUGIN_JSON" 2>/dev/null; then fail "plugin.json contains 'hooks' key (should be in hooks/hooks.json only)" else pass "plugin.json does not contain 'hooks' key" fi echo "" # ------------------------------------------------------- # Check 2: hooks.json # ------------------------------------------------------- echo "--- Check 2: hooks.json ---" HOOKS_JSON="$PLUGIN_ROOT/hooks/hooks.json" if [ -f "$HOOKS_JSON" ]; then pass "hooks.json exists" else fail "hooks.json missing at hooks/hooks.json" fi if node -e "JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8'))" 2>/dev/null; then pass "hooks.json is valid JSON" else fail "hooks.json is invalid JSON" fi # Check structure: hooks key is an object if node -e " const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8')); if (!h.hooks || typeof h.hooks !== 'object') process.exit(1); " 2>/dev/null; then pass "hooks.json has 'hooks' object at root" else fail "hooks.json missing root 'hooks' object" fi echo "" # ------------------------------------------------------- # Check 3: Script references # ------------------------------------------------------- echo "--- Check 3: Script References ---" # Extract script paths from hooks.json SCRIPT_PATHS=$(node -e " const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8')); for (const [event, handlers] of Object.entries(h.hooks)) { for (const handler of handlers) { for (const hook of (handler.hooks || [])) { if (hook.command) { const match = hook.command.match(/\\\$\\{CLAUDE_PLUGIN_ROOT\\}\\/(.+)/); if (match) console.log(match[1]); } } } } " 2>/dev/null) for script_path in $SCRIPT_PATHS; do full_path="$PLUGIN_ROOT/$script_path" if [ -f "$full_path" ]; then pass "Script exists: $script_path" else fail "Script missing: $script_path" fi done echo "" # ------------------------------------------------------- # Check 4: Event names # ------------------------------------------------------- echo "--- Check 4: Event Names ---" VALID_EVENTS="SessionStart UserPromptSubmit PreToolUse PostToolUse Stop" HOOK_EVENTS=$(node -e " const h = JSON.parse(require('fs').readFileSync('$HOOKS_JSON', 'utf-8')); for (const event of Object.keys(h.hooks)) console.log(event); " 2>/dev/null) for event in $HOOK_EVENTS; do if echo "$VALID_EVENTS" | grep -qw "$event"; then pass "Valid event: $event" else fail "Invalid event: $event" fi done echo "" # ------------------------------------------------------- # Check 5: Documentation # ------------------------------------------------------- echo "--- Check 5: Documentation ---" CLAUDE_MD="$PLUGIN_ROOT/CLAUDE.md" if grep -q "## Hooks" "$CLAUDE_MD" 2>/dev/null; then pass "CLAUDE.md has Hooks section" else fail "CLAUDE.md missing Hooks section" fi for script in pre-edit-secrets session-start-context stop-assessment-reminder; do if grep -q "$script" "$CLAUDE_MD" 2>/dev/null; then pass "CLAUDE.md documents $script" else fail "CLAUDE.md missing $script documentation" fi done echo "" # ------------------------------------------------------- # Summary # ------------------------------------------------------- TOTAL=$((PASS + FAIL)) echo "=== Results: $PASS/$TOTAL passed, $FAIL failed ===" if [ "$FAIL" -gt 0 ]; then exit 1 fi