| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- #!/bin/bash
- # Hook Testing Helper
- # Tests a hook with sample input and shows output
- set -euo pipefail
- # Usage
- show_usage() {
- echo "Usage: $0 [options] <hook-script> <test-input.json>"
- echo ""
- echo "Options:"
- echo " -h, --help Show this help message"
- echo " -v, --verbose Show detailed execution information"
- echo " -t, --timeout N Set timeout in seconds (default: 60)"
- echo ""
- echo "Examples:"
- echo " $0 validate-bash.sh test-input.json"
- echo " $0 -v -t 30 validate-write.sh write-input.json"
- echo ""
- echo "Creates sample test input with:"
- echo " $0 --create-sample <event-type>"
- exit 0
- }
- # Create sample input
- create_sample() {
- event_type="$1"
- case "$event_type" in
- PreToolUse)
- cat <<'EOF'
- {
- "session_id": "test-session",
- "transcript_path": "/tmp/transcript.txt",
- "cwd": "/tmp/test-project",
- "permission_mode": "ask",
- "hook_event_name": "PreToolUse",
- "tool_name": "Write",
- "tool_input": {
- "file_path": "/tmp/test.txt",
- "content": "Test content"
- }
- }
- EOF
- ;;
- PostToolUse)
- cat <<'EOF'
- {
- "session_id": "test-session",
- "transcript_path": "/tmp/transcript.txt",
- "cwd": "/tmp/test-project",
- "permission_mode": "ask",
- "hook_event_name": "PostToolUse",
- "tool_name": "Bash",
- "tool_result": "Command executed successfully"
- }
- EOF
- ;;
- Stop|SubagentStop)
- cat <<'EOF'
- {
- "session_id": "test-session",
- "transcript_path": "/tmp/transcript.txt",
- "cwd": "/tmp/test-project",
- "permission_mode": "ask",
- "hook_event_name": "Stop",
- "reason": "Task appears complete"
- }
- EOF
- ;;
- UserPromptSubmit)
- cat <<'EOF'
- {
- "session_id": "test-session",
- "transcript_path": "/tmp/transcript.txt",
- "cwd": "/tmp/test-project",
- "permission_mode": "ask",
- "hook_event_name": "UserPromptSubmit",
- "user_prompt": "Test user prompt"
- }
- EOF
- ;;
- SessionStart|SessionEnd)
- cat <<'EOF'
- {
- "session_id": "test-session",
- "transcript_path": "/tmp/transcript.txt",
- "cwd": "/tmp/test-project",
- "permission_mode": "ask",
- "hook_event_name": "SessionStart"
- }
- EOF
- ;;
- *)
- echo "Unknown event type: $event_type"
- echo "Valid types: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit, SessionStart, SessionEnd"
- exit 1
- ;;
- esac
- }
- # Parse arguments
- VERBOSE=false
- TIMEOUT=60
- while [ $# -gt 0 ]; do
- case "$1" in
- -h|--help)
- show_usage
- ;;
- -v|--verbose)
- VERBOSE=true
- shift
- ;;
- -t|--timeout)
- TIMEOUT="$2"
- shift 2
- ;;
- --create-sample)
- create_sample "$2"
- exit 0
- ;;
- *)
- break
- ;;
- esac
- done
- if [ $# -ne 2 ]; then
- echo "Error: Missing required arguments"
- echo ""
- show_usage
- fi
- HOOK_SCRIPT="$1"
- TEST_INPUT="$2"
- # Validate inputs
- if [ ! -f "$HOOK_SCRIPT" ]; then
- echo "❌ Error: Hook script not found: $HOOK_SCRIPT"
- exit 1
- fi
- if [ ! -x "$HOOK_SCRIPT" ]; then
- echo "⚠️ Warning: Hook script is not executable. Attempting to run with bash..."
- HOOK_SCRIPT="bash $HOOK_SCRIPT"
- fi
- if [ ! -f "$TEST_INPUT" ]; then
- echo "❌ Error: Test input not found: $TEST_INPUT"
- exit 1
- fi
- # Validate test input JSON
- if ! jq empty "$TEST_INPUT" 2>/dev/null; then
- echo "❌ Error: Test input is not valid JSON"
- exit 1
- fi
- echo "🧪 Testing hook: $HOOK_SCRIPT"
- echo "📥 Input: $TEST_INPUT"
- echo ""
- if [ "$VERBOSE" = true ]; then
- echo "Input JSON:"
- jq . "$TEST_INPUT"
- echo ""
- fi
- # Set up environment
- export CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-/tmp/test-project}"
- export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(pwd)}"
- export CLAUDE_ENV_FILE="${CLAUDE_ENV_FILE:-/tmp/test-env-$$}"
- if [ "$VERBOSE" = true ]; then
- echo "Environment:"
- echo " CLAUDE_PROJECT_DIR=$CLAUDE_PROJECT_DIR"
- echo " CLAUDE_PLUGIN_ROOT=$CLAUDE_PLUGIN_ROOT"
- echo " CLAUDE_ENV_FILE=$CLAUDE_ENV_FILE"
- echo ""
- fi
- # Run the hook
- echo "▶️ Running hook (timeout: ${TIMEOUT}s)..."
- echo ""
- start_time=$(date +%s)
- set +e
- output=$(timeout "$TIMEOUT" bash -c "cat '$TEST_INPUT' | $HOOK_SCRIPT" 2>&1)
- exit_code=$?
- set -e
- end_time=$(date +%s)
- duration=$((end_time - start_time))
- # Analyze results
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- echo "Results:"
- echo ""
- echo "Exit Code: $exit_code"
- echo "Duration: ${duration}s"
- echo ""
- case $exit_code in
- 0)
- echo "✅ Hook approved/succeeded"
- ;;
- 2)
- echo "🚫 Hook blocked/denied"
- ;;
- 124)
- echo "⏱️ Hook timed out after ${TIMEOUT}s"
- ;;
- *)
- echo "⚠️ Hook returned unexpected exit code: $exit_code"
- ;;
- esac
- echo ""
- echo "Output:"
- if [ -n "$output" ]; then
- echo "$output"
- echo ""
- # Try to parse as JSON
- if echo "$output" | jq empty 2>/dev/null; then
- echo "Parsed JSON output:"
- echo "$output" | jq .
- fi
- else
- echo "(no output)"
- fi
- # Check for environment file
- if [ -f "$CLAUDE_ENV_FILE" ]; then
- echo ""
- echo "Environment file created:"
- cat "$CLAUDE_ENV_FILE"
- rm -f "$CLAUDE_ENV_FILE"
- fi
- echo ""
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- if [ $exit_code -eq 0 ] || [ $exit_code -eq 2 ]; then
- echo "✅ Test completed successfully"
- exit 0
- else
- echo "❌ Test failed"
- exit 1
- fi
|