test-hook.sh 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/bin/bash
  2. # Hook Testing Helper
  3. # Tests a hook with sample input and shows output
  4. set -euo pipefail
  5. # Usage
  6. show_usage() {
  7. echo "Usage: $0 [options] <hook-script> <test-input.json>"
  8. echo ""
  9. echo "Options:"
  10. echo " -h, --help Show this help message"
  11. echo " -v, --verbose Show detailed execution information"
  12. echo " -t, --timeout N Set timeout in seconds (default: 60)"
  13. echo ""
  14. echo "Examples:"
  15. echo " $0 validate-bash.sh test-input.json"
  16. echo " $0 -v -t 30 validate-write.sh write-input.json"
  17. echo ""
  18. echo "Creates sample test input with:"
  19. echo " $0 --create-sample <event-type>"
  20. exit 0
  21. }
  22. # Create sample input
  23. create_sample() {
  24. event_type="$1"
  25. case "$event_type" in
  26. PreToolUse)
  27. cat <<'EOF'
  28. {
  29. "session_id": "test-session",
  30. "transcript_path": "/tmp/transcript.txt",
  31. "cwd": "/tmp/test-project",
  32. "permission_mode": "ask",
  33. "hook_event_name": "PreToolUse",
  34. "tool_name": "Write",
  35. "tool_input": {
  36. "file_path": "/tmp/test.txt",
  37. "content": "Test content"
  38. }
  39. }
  40. EOF
  41. ;;
  42. PostToolUse)
  43. cat <<'EOF'
  44. {
  45. "session_id": "test-session",
  46. "transcript_path": "/tmp/transcript.txt",
  47. "cwd": "/tmp/test-project",
  48. "permission_mode": "ask",
  49. "hook_event_name": "PostToolUse",
  50. "tool_name": "Bash",
  51. "tool_result": "Command executed successfully"
  52. }
  53. EOF
  54. ;;
  55. Stop|SubagentStop)
  56. cat <<'EOF'
  57. {
  58. "session_id": "test-session",
  59. "transcript_path": "/tmp/transcript.txt",
  60. "cwd": "/tmp/test-project",
  61. "permission_mode": "ask",
  62. "hook_event_name": "Stop",
  63. "reason": "Task appears complete"
  64. }
  65. EOF
  66. ;;
  67. UserPromptSubmit)
  68. cat <<'EOF'
  69. {
  70. "session_id": "test-session",
  71. "transcript_path": "/tmp/transcript.txt",
  72. "cwd": "/tmp/test-project",
  73. "permission_mode": "ask",
  74. "hook_event_name": "UserPromptSubmit",
  75. "user_prompt": "Test user prompt"
  76. }
  77. EOF
  78. ;;
  79. SessionStart|SessionEnd)
  80. cat <<'EOF'
  81. {
  82. "session_id": "test-session",
  83. "transcript_path": "/tmp/transcript.txt",
  84. "cwd": "/tmp/test-project",
  85. "permission_mode": "ask",
  86. "hook_event_name": "SessionStart"
  87. }
  88. EOF
  89. ;;
  90. *)
  91. echo "Unknown event type: $event_type"
  92. echo "Valid types: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit, SessionStart, SessionEnd"
  93. exit 1
  94. ;;
  95. esac
  96. }
  97. # Parse arguments
  98. VERBOSE=false
  99. TIMEOUT=60
  100. while [ $# -gt 0 ]; do
  101. case "$1" in
  102. -h|--help)
  103. show_usage
  104. ;;
  105. -v|--verbose)
  106. VERBOSE=true
  107. shift
  108. ;;
  109. -t|--timeout)
  110. TIMEOUT="$2"
  111. shift 2
  112. ;;
  113. --create-sample)
  114. create_sample "$2"
  115. exit 0
  116. ;;
  117. *)
  118. break
  119. ;;
  120. esac
  121. done
  122. if [ $# -ne 2 ]; then
  123. echo "Error: Missing required arguments"
  124. echo ""
  125. show_usage
  126. fi
  127. HOOK_SCRIPT="$1"
  128. TEST_INPUT="$2"
  129. # Validate inputs
  130. if [ ! -f "$HOOK_SCRIPT" ]; then
  131. echo "❌ Error: Hook script not found: $HOOK_SCRIPT"
  132. exit 1
  133. fi
  134. if [ ! -x "$HOOK_SCRIPT" ]; then
  135. echo "⚠️ Warning: Hook script is not executable. Attempting to run with bash..."
  136. HOOK_SCRIPT="bash $HOOK_SCRIPT"
  137. fi
  138. if [ ! -f "$TEST_INPUT" ]; then
  139. echo "❌ Error: Test input not found: $TEST_INPUT"
  140. exit 1
  141. fi
  142. # Validate test input JSON
  143. if ! jq empty "$TEST_INPUT" 2>/dev/null; then
  144. echo "❌ Error: Test input is not valid JSON"
  145. exit 1
  146. fi
  147. echo "🧪 Testing hook: $HOOK_SCRIPT"
  148. echo "📥 Input: $TEST_INPUT"
  149. echo ""
  150. if [ "$VERBOSE" = true ]; then
  151. echo "Input JSON:"
  152. jq . "$TEST_INPUT"
  153. echo ""
  154. fi
  155. # Set up environment
  156. export CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-/tmp/test-project}"
  157. export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(pwd)}"
  158. export CLAUDE_ENV_FILE="${CLAUDE_ENV_FILE:-/tmp/test-env-$$}"
  159. if [ "$VERBOSE" = true ]; then
  160. echo "Environment:"
  161. echo " CLAUDE_PROJECT_DIR=$CLAUDE_PROJECT_DIR"
  162. echo " CLAUDE_PLUGIN_ROOT=$CLAUDE_PLUGIN_ROOT"
  163. echo " CLAUDE_ENV_FILE=$CLAUDE_ENV_FILE"
  164. echo ""
  165. fi
  166. # Run the hook
  167. echo "▶️ Running hook (timeout: ${TIMEOUT}s)..."
  168. echo ""
  169. start_time=$(date +%s)
  170. set +e
  171. output=$(timeout "$TIMEOUT" bash -c "cat '$TEST_INPUT' | $HOOK_SCRIPT" 2>&1)
  172. exit_code=$?
  173. set -e
  174. end_time=$(date +%s)
  175. duration=$((end_time - start_time))
  176. # Analyze results
  177. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  178. echo "Results:"
  179. echo ""
  180. echo "Exit Code: $exit_code"
  181. echo "Duration: ${duration}s"
  182. echo ""
  183. case $exit_code in
  184. 0)
  185. echo "✅ Hook approved/succeeded"
  186. ;;
  187. 2)
  188. echo "🚫 Hook blocked/denied"
  189. ;;
  190. 124)
  191. echo "⏱️ Hook timed out after ${TIMEOUT}s"
  192. ;;
  193. *)
  194. echo "⚠️ Hook returned unexpected exit code: $exit_code"
  195. ;;
  196. esac
  197. echo ""
  198. echo "Output:"
  199. if [ -n "$output" ]; then
  200. echo "$output"
  201. echo ""
  202. # Try to parse as JSON
  203. if echo "$output" | jq empty 2>/dev/null; then
  204. echo "Parsed JSON output:"
  205. echo "$output" | jq .
  206. fi
  207. else
  208. echo "(no output)"
  209. fi
  210. # Check for environment file
  211. if [ -f "$CLAUDE_ENV_FILE" ]; then
  212. echo ""
  213. echo "Environment file created:"
  214. cat "$CLAUDE_ENV_FILE"
  215. rm -f "$CLAUDE_ENV_FILE"
  216. fi
  217. echo ""
  218. echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  219. if [ $exit_code -eq 0 ] || [ $exit_code -eq 2 ]; then
  220. echo "✅ Test completed successfully"
  221. exit 0
  222. else
  223. echo "❌ Test failed"
  224. exit 1
  225. fi