| #!/bin/bash |
| |
| |
|
|
| set -euo pipefail |
|
|
| |
| 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() { |
| 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 |
| } |
|
|
| |
| 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" |
|
|
| |
| 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 |
|
|
| |
| 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 |
|
|
| |
| 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 |
|
|
| |
| 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)) |
|
|
| |
| 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 "" |
|
|
| |
| if echo "$output" | jq empty 2>/dev/null; then |
| echo "Parsed JSON output:" |
| echo "$output" | jq . |
| fi |
| else |
| echo "(no output)" |
| fi |
|
|
| |
| 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 |
|
|