clasp-ai 0.41.2 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/install.js +1 -1
- package/scripts/test-suite.sh +597 -0
package/package.json
CHANGED
package/scripts/install.js
CHANGED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CLASP Comprehensive Test Suite
|
|
3
|
+
# Runs isolated tests in separate tmux sessions with proper cleanup
|
|
4
|
+
#
|
|
5
|
+
# Tests:
|
|
6
|
+
# 1. Tool Calling - Validates Task tool schema filtering with optional params
|
|
7
|
+
# 2. Statusline - Validates Claude Code status line integration
|
|
8
|
+
# 3. Proxy Lifecycle - Tests start/stop/status commands
|
|
9
|
+
#
|
|
10
|
+
# Each test runs in its own tmux session that is destroyed and recreated
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
CLASP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
16
|
+
RESULTS_DIR="$CLASP_DIR/test-results"
|
|
17
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
18
|
+
RESULTS_FILE="$RESULTS_DIR/test-run-$TIMESTAMP.log"
|
|
19
|
+
SESSION_PREFIX="clasp-test"
|
|
20
|
+
|
|
21
|
+
# Colors for output
|
|
22
|
+
RED='\033[0;31m'
|
|
23
|
+
GREEN='\033[0;32m'
|
|
24
|
+
YELLOW='\033[1;33m'
|
|
25
|
+
BLUE='\033[0;34m'
|
|
26
|
+
MAGENTA='\033[0;35m'
|
|
27
|
+
CYAN='\033[0;36m'
|
|
28
|
+
NC='\033[0m'
|
|
29
|
+
BOLD='\033[1m'
|
|
30
|
+
|
|
31
|
+
# Test counters
|
|
32
|
+
TESTS_PASSED=0
|
|
33
|
+
TESTS_FAILED=0
|
|
34
|
+
TESTS_SKIPPED=0
|
|
35
|
+
|
|
36
|
+
# Create results directory
|
|
37
|
+
mkdir -p "$RESULTS_DIR"
|
|
38
|
+
|
|
39
|
+
#######################################
|
|
40
|
+
# Utility Functions
|
|
41
|
+
#######################################
|
|
42
|
+
|
|
43
|
+
log() {
|
|
44
|
+
echo -e "$1" | tee -a "$RESULTS_FILE"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
log_section() {
|
|
48
|
+
log ""
|
|
49
|
+
log "${MAGENTA}${BOLD}════════════════════════════════════════════════════════════════${NC}"
|
|
50
|
+
log "${MAGENTA}${BOLD} $1${NC}"
|
|
51
|
+
log "${MAGENTA}${BOLD}════════════════════════════════════════════════════════════════${NC}"
|
|
52
|
+
log ""
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log_test_start() {
|
|
56
|
+
log "${CYAN}▶ Starting test: $1${NC}"
|
|
57
|
+
log " Description: $2"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
log_pass() {
|
|
61
|
+
log "${GREEN}${BOLD}✓ PASS:${NC} $1"
|
|
62
|
+
((TESTS_PASSED++))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
log_fail() {
|
|
66
|
+
log "${RED}${BOLD}✗ FAIL:${NC} $1"
|
|
67
|
+
log "${RED} Reason: $2${NC}"
|
|
68
|
+
((TESTS_FAILED++))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
log_skip() {
|
|
72
|
+
log "${YELLOW}○ SKIP:${NC} $1"
|
|
73
|
+
log "${YELLOW} Reason: $2${NC}"
|
|
74
|
+
((TESTS_SKIPPED++))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# Cleanup any existing test sessions
|
|
78
|
+
cleanup_test_sessions() {
|
|
79
|
+
log "${YELLOW}Cleaning up existing test sessions...${NC}"
|
|
80
|
+
for session in $(tmux ls -F '#{session_name}' 2>/dev/null | grep "^${SESSION_PREFIX}" || true); do
|
|
81
|
+
log " Killing session: $session"
|
|
82
|
+
tmux kill-session -t "$session" 2>/dev/null || true
|
|
83
|
+
done
|
|
84
|
+
# Also cleanup zombie CLASP processes
|
|
85
|
+
pkill -9 -f "clasp.*-profile" 2>/dev/null || true
|
|
86
|
+
# Clean stale status files
|
|
87
|
+
rm -f ~/.clasp/status/*.json 2>/dev/null || true
|
|
88
|
+
rm -f ~/.clasp/status.json 2>/dev/null || true
|
|
89
|
+
sleep 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Create and verify tmux session
|
|
93
|
+
create_test_session() {
|
|
94
|
+
local session_name="$1"
|
|
95
|
+
local work_dir="${2:-$CLASP_DIR}"
|
|
96
|
+
|
|
97
|
+
# Ensure no existing session with this name
|
|
98
|
+
tmux kill-session -t "$session_name" 2>/dev/null || true
|
|
99
|
+
sleep 0.5
|
|
100
|
+
|
|
101
|
+
# Create new session
|
|
102
|
+
tmux new-session -d -s "$session_name" -c "$work_dir" -x 200 -y 50
|
|
103
|
+
|
|
104
|
+
if tmux has-session -t "$session_name" 2>/dev/null; then
|
|
105
|
+
log " Created tmux session: $session_name"
|
|
106
|
+
return 0
|
|
107
|
+
else
|
|
108
|
+
log "${RED} Failed to create tmux session: $session_name${NC}"
|
|
109
|
+
return 1
|
|
110
|
+
fi
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Destroy tmux session with verification
|
|
114
|
+
destroy_test_session() {
|
|
115
|
+
local session_name="$1"
|
|
116
|
+
|
|
117
|
+
if tmux has-session -t "$session_name" 2>/dev/null; then
|
|
118
|
+
# First try graceful termination
|
|
119
|
+
tmux send-keys -t "$session_name" C-c 2>/dev/null || true
|
|
120
|
+
sleep 1
|
|
121
|
+
|
|
122
|
+
# Force kill
|
|
123
|
+
tmux kill-session -t "$session_name" 2>/dev/null || true
|
|
124
|
+
log " Destroyed tmux session: $session_name"
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
# Kill any associated CLASP processes
|
|
128
|
+
pkill -9 -f "clasp" 2>/dev/null || true
|
|
129
|
+
sleep 0.5
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Wait for condition with timeout
|
|
133
|
+
wait_for_condition() {
|
|
134
|
+
local condition="$1"
|
|
135
|
+
local timeout="${2:-30}"
|
|
136
|
+
local interval="${3:-1}"
|
|
137
|
+
local elapsed=0
|
|
138
|
+
|
|
139
|
+
while [ $elapsed -lt $timeout ]; do
|
|
140
|
+
if eval "$condition" 2>/dev/null; then
|
|
141
|
+
return 0
|
|
142
|
+
fi
|
|
143
|
+
sleep $interval
|
|
144
|
+
((elapsed+=interval))
|
|
145
|
+
done
|
|
146
|
+
return 1
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#######################################
|
|
150
|
+
# Test 1: Statusline Integration
|
|
151
|
+
#######################################
|
|
152
|
+
|
|
153
|
+
test_statusline() {
|
|
154
|
+
local session_name="${SESSION_PREFIX}-statusline"
|
|
155
|
+
local test_port=8081
|
|
156
|
+
local test_name="Statusline Integration"
|
|
157
|
+
|
|
158
|
+
log_test_start "$test_name" "Validates Claude Code status line displays CLASP proxy info"
|
|
159
|
+
|
|
160
|
+
# Pre-test cleanup
|
|
161
|
+
destroy_test_session "$session_name"
|
|
162
|
+
rm -f ~/.clasp/status/${test_port}.json 2>/dev/null
|
|
163
|
+
|
|
164
|
+
# Check prerequisites
|
|
165
|
+
if [[ ! -x "$CLASP_DIR/bin/clasp" ]]; then
|
|
166
|
+
log_skip "$test_name" "CLASP binary not found. Run: go build -o bin/clasp ./cmd/clasp"
|
|
167
|
+
return
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
if [[ ! -f ~/.claude/clasp-statusline.sh ]]; then
|
|
171
|
+
log_skip "$test_name" "Statusline script not installed at ~/.claude/clasp-statusline.sh"
|
|
172
|
+
return
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Create test session
|
|
176
|
+
if ! create_test_session "$session_name"; then
|
|
177
|
+
log_fail "$test_name" "Could not create tmux session"
|
|
178
|
+
return
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# Step 1: Start CLASP proxy
|
|
182
|
+
log " Step 1: Starting CLASP proxy on port $test_port..."
|
|
183
|
+
tmux send-keys -t "$session_name" "cd $CLASP_DIR && ./bin/clasp --port $test_port --proxy-only 2>&1 &" Enter
|
|
184
|
+
tmux send-keys -t "$session_name" "CLASP_PID=\$!" Enter
|
|
185
|
+
|
|
186
|
+
# Wait for status file to be created
|
|
187
|
+
log " Step 2: Waiting for status file..."
|
|
188
|
+
if ! wait_for_condition "[ -f ~/.clasp/status/${test_port}.json ]" 10; then
|
|
189
|
+
log_fail "$test_name" "Status file not created at ~/.clasp/status/${test_port}.json"
|
|
190
|
+
destroy_test_session "$session_name"
|
|
191
|
+
return
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Step 3: Verify status file content
|
|
195
|
+
log " Step 3: Verifying status file content..."
|
|
196
|
+
local status_content
|
|
197
|
+
status_content=$(cat ~/.clasp/status/${test_port}.json 2>/dev/null)
|
|
198
|
+
|
|
199
|
+
if ! echo "$status_content" | jq -e '.running == true' >/dev/null 2>&1; then
|
|
200
|
+
log_fail "$test_name" "Status file does not show running=true"
|
|
201
|
+
destroy_test_session "$session_name"
|
|
202
|
+
return
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
if ! echo "$status_content" | jq -e ".port == $test_port" >/dev/null 2>&1; then
|
|
206
|
+
log_fail "$test_name" "Status file does not show correct port"
|
|
207
|
+
destroy_test_session "$session_name"
|
|
208
|
+
return
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
# Step 4: Test statusline script
|
|
212
|
+
log " Step 4: Testing statusline script..."
|
|
213
|
+
local statusline_output
|
|
214
|
+
statusline_output=$(ANTHROPIC_BASE_URL="http://localhost:$test_port" ~/.claude/clasp-statusline.sh '{}' 2>&1)
|
|
215
|
+
|
|
216
|
+
if [[ "$statusline_output" == *"[CLASP:$test_port]"* ]]; then
|
|
217
|
+
log " Statusline output: $statusline_output"
|
|
218
|
+
log_pass "$test_name"
|
|
219
|
+
else
|
|
220
|
+
log_fail "$test_name" "Statusline script did not produce expected output. Got: $statusline_output"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Step 5: Test process detection (verify PID check works)
|
|
224
|
+
log " Step 5: Verifying process detection..."
|
|
225
|
+
local pid
|
|
226
|
+
pid=$(echo "$status_content" | jq -r '.pid')
|
|
227
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
228
|
+
log " Process $pid is running correctly"
|
|
229
|
+
else
|
|
230
|
+
log "${YELLOW} Warning: Process $pid not running (may have exited)${NC}"
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Cleanup
|
|
234
|
+
destroy_test_session "$session_name"
|
|
235
|
+
rm -f ~/.clasp/status/${test_port}.json 2>/dev/null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
#######################################
|
|
239
|
+
# Test 2: Tool Schema Filtering
|
|
240
|
+
#######################################
|
|
241
|
+
|
|
242
|
+
test_tool_calling() {
|
|
243
|
+
local session_name="${SESSION_PREFIX}-tools"
|
|
244
|
+
local test_port=8082
|
|
245
|
+
local test_name="Tool Schema Filtering"
|
|
246
|
+
|
|
247
|
+
log_test_start "$test_name" "Validates strict:false and required array filtering for Responses API"
|
|
248
|
+
|
|
249
|
+
# Pre-test cleanup
|
|
250
|
+
destroy_test_session "$session_name"
|
|
251
|
+
|
|
252
|
+
# Check prerequisites
|
|
253
|
+
if [[ ! -x "$CLASP_DIR/bin/clasp" ]]; then
|
|
254
|
+
log_skip "$test_name" "CLASP binary not found. Run: go build -o bin/clasp ./cmd/clasp"
|
|
255
|
+
return
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
if [[ -z "$OPENAI_API_KEY" ]]; then
|
|
259
|
+
if [[ -f "$CLASP_DIR/.env" ]]; then
|
|
260
|
+
source "$CLASP_DIR/.env"
|
|
261
|
+
fi
|
|
262
|
+
if [[ -z "$OPENAI_API_KEY" ]]; then
|
|
263
|
+
log_skip "$test_name" "OPENAI_API_KEY not set"
|
|
264
|
+
return
|
|
265
|
+
fi
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
# Create test session
|
|
269
|
+
if ! create_test_session "$session_name"; then
|
|
270
|
+
log_fail "$test_name" "Could not create tmux session"
|
|
271
|
+
return
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
# Step 1: Start CLASP with debug mode
|
|
275
|
+
log " Step 1: Starting CLASP with debug logging..."
|
|
276
|
+
local debug_log="$RESULTS_DIR/tool-debug-$TIMESTAMP.log"
|
|
277
|
+
tmux send-keys -t "$session_name" "cd $CLASP_DIR" Enter
|
|
278
|
+
tmux send-keys -t "$session_name" "OPENAI_API_KEY=$OPENAI_API_KEY ./bin/clasp --port $test_port --provider openai --debug --proxy-only 2>&1 | tee $debug_log &" Enter
|
|
279
|
+
|
|
280
|
+
# Wait for CLASP to start
|
|
281
|
+
if ! wait_for_condition "[ -f ~/.clasp/status/${test_port}.json ]" 10; then
|
|
282
|
+
log_fail "$test_name" "CLASP did not start (no status file)"
|
|
283
|
+
destroy_test_session "$session_name"
|
|
284
|
+
return
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
# Step 2: Send a test request with tools
|
|
288
|
+
log " Step 2: Sending test request with tool definitions..."
|
|
289
|
+
local test_request='{"model":"claude-3-5-sonnet-20241022","max_tokens":100,"messages":[{"role":"user","content":"test"}],"tools":[{"name":"test_tool","description":"test","input_schema":{"type":"object","properties":{"required_param":{"type":"string"},"optional_param":{"type":"boolean","description":"Optional flag"}},"required":["required_param","optional_param"],"strict":true}}]}'
|
|
290
|
+
|
|
291
|
+
local response
|
|
292
|
+
response=$(curl -s -X POST "http://localhost:$test_port/v1/messages" \
|
|
293
|
+
-H "Content-Type: application/json" \
|
|
294
|
+
-H "x-api-key: test" \
|
|
295
|
+
-H "anthropic-version: 2023-06-01" \
|
|
296
|
+
-d "$test_request" 2>&1)
|
|
297
|
+
|
|
298
|
+
# Step 3: Check debug log for schema transformation
|
|
299
|
+
log " Step 3: Verifying schema transformation in debug log..."
|
|
300
|
+
sleep 2
|
|
301
|
+
|
|
302
|
+
# Check that strict:false is set at top level
|
|
303
|
+
if grep -q '"strict":false' "$debug_log" 2>/dev/null; then
|
|
304
|
+
log " ✓ Found strict:false in transformed request"
|
|
305
|
+
else
|
|
306
|
+
log "${YELLOW} ⚠ Could not verify strict:false (may need more request data)${NC}"
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
# Check that required array is filtered
|
|
310
|
+
if grep -q '"required":\["required_param"\]' "$debug_log" 2>/dev/null || \
|
|
311
|
+
! grep -q '"optional_param".*"required"' "$debug_log" 2>/dev/null; then
|
|
312
|
+
log " ✓ Required array appears to be filtered correctly"
|
|
313
|
+
fi
|
|
314
|
+
|
|
315
|
+
# Step 4: Check for validation errors
|
|
316
|
+
if grep -qi "invalid tool parameters" "$debug_log" 2>/dev/null; then
|
|
317
|
+
log_fail "$test_name" "Found 'Invalid tool parameters' error in logs"
|
|
318
|
+
elif grep -qi "missing required" "$debug_log" 2>/dev/null; then
|
|
319
|
+
log_fail "$test_name" "Found 'missing required' error in logs"
|
|
320
|
+
else
|
|
321
|
+
log_pass "$test_name"
|
|
322
|
+
fi
|
|
323
|
+
|
|
324
|
+
# Cleanup
|
|
325
|
+
destroy_test_session "$session_name"
|
|
326
|
+
rm -f ~/.clasp/status/${test_port}.json 2>/dev/null
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#######################################
|
|
330
|
+
# Test 3: Proxy Lifecycle
|
|
331
|
+
#######################################
|
|
332
|
+
|
|
333
|
+
test_proxy_lifecycle() {
|
|
334
|
+
local session_name="${SESSION_PREFIX}-lifecycle"
|
|
335
|
+
local test_port=8083
|
|
336
|
+
local test_name="Proxy Lifecycle"
|
|
337
|
+
|
|
338
|
+
log_test_start "$test_name" "Validates CLASP start/stop/status commands"
|
|
339
|
+
|
|
340
|
+
# Pre-test cleanup
|
|
341
|
+
destroy_test_session "$session_name"
|
|
342
|
+
rm -f ~/.clasp/status/${test_port}.json 2>/dev/null
|
|
343
|
+
|
|
344
|
+
# Check prerequisites
|
|
345
|
+
if [[ ! -x "$CLASP_DIR/bin/clasp" ]]; then
|
|
346
|
+
log_skip "$test_name" "CLASP binary not found"
|
|
347
|
+
return
|
|
348
|
+
fi
|
|
349
|
+
|
|
350
|
+
# Create test session
|
|
351
|
+
if ! create_test_session "$session_name"; then
|
|
352
|
+
log_fail "$test_name" "Could not create tmux session"
|
|
353
|
+
return
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
# Step 1: Check initial status (should show not running)
|
|
357
|
+
log " Step 1: Checking initial status..."
|
|
358
|
+
local initial_status
|
|
359
|
+
initial_status=$("$CLASP_DIR/bin/clasp" --status --port $test_port 2>&1 || true)
|
|
360
|
+
|
|
361
|
+
if [[ "$initial_status" == *"not running"* ]] || [[ "$initial_status" == *"No running"* ]]; then
|
|
362
|
+
log " ✓ Initial status correctly shows not running"
|
|
363
|
+
fi
|
|
364
|
+
|
|
365
|
+
# Step 2: Start proxy
|
|
366
|
+
log " Step 2: Starting proxy..."
|
|
367
|
+
tmux send-keys -t "$session_name" "cd $CLASP_DIR && ./bin/clasp --port $test_port --proxy-only 2>&1 &" Enter
|
|
368
|
+
|
|
369
|
+
if ! wait_for_condition "[ -f ~/.clasp/status/${test_port}.json ]" 10; then
|
|
370
|
+
log_fail "$test_name" "Proxy did not start"
|
|
371
|
+
destroy_test_session "$session_name"
|
|
372
|
+
return
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
# Step 3: Check running status
|
|
376
|
+
log " Step 3: Checking running status..."
|
|
377
|
+
local running_status
|
|
378
|
+
running_status=$("$CLASP_DIR/bin/clasp" --status --port $test_port 2>&1 || true)
|
|
379
|
+
|
|
380
|
+
if [[ "$running_status" == *"Running"* ]] || [[ "$running_status" == *"[CLASP:$test_port]"* ]]; then
|
|
381
|
+
log " ✓ Status correctly shows proxy running"
|
|
382
|
+
else
|
|
383
|
+
log "${YELLOW} ⚠ Status output: $running_status${NC}"
|
|
384
|
+
fi
|
|
385
|
+
|
|
386
|
+
# Step 4: Verify HTTP endpoint
|
|
387
|
+
log " Step 4: Checking HTTP endpoint..."
|
|
388
|
+
local health_check
|
|
389
|
+
health_check=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:$test_port/health" 2>&1 || echo "failed")
|
|
390
|
+
|
|
391
|
+
if [[ "$health_check" == "200" ]]; then
|
|
392
|
+
log " ✓ Health endpoint responding (HTTP 200)"
|
|
393
|
+
else
|
|
394
|
+
log "${YELLOW} ⚠ Health check returned: $health_check${NC}"
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
# Step 5: Stop proxy and verify cleanup
|
|
398
|
+
log " Step 5: Stopping proxy..."
|
|
399
|
+
tmux send-keys -t "$session_name" "kill %1" Enter
|
|
400
|
+
sleep 2
|
|
401
|
+
|
|
402
|
+
# Verify status file is removed (or shows not running)
|
|
403
|
+
if [[ ! -f ~/.clasp/status/${test_port}.json ]]; then
|
|
404
|
+
log " ✓ Status file cleaned up on shutdown"
|
|
405
|
+
log_pass "$test_name"
|
|
406
|
+
else
|
|
407
|
+
local final_status
|
|
408
|
+
final_status=$(cat ~/.clasp/status/${test_port}.json 2>/dev/null)
|
|
409
|
+
if echo "$final_status" | jq -e '.running == false' >/dev/null 2>&1; then
|
|
410
|
+
log " ✓ Status file shows running=false"
|
|
411
|
+
log_pass "$test_name"
|
|
412
|
+
else
|
|
413
|
+
log_fail "$test_name" "Status file not cleaned up properly"
|
|
414
|
+
fi
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
# Cleanup
|
|
418
|
+
destroy_test_session "$session_name"
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#######################################
|
|
422
|
+
# Test 4: Multi-Instance Statusline
|
|
423
|
+
#######################################
|
|
424
|
+
|
|
425
|
+
test_multi_instance() {
|
|
426
|
+
local session_name="${SESSION_PREFIX}-multi"
|
|
427
|
+
local test_name="Multi-Instance Statusline"
|
|
428
|
+
local port1=8084
|
|
429
|
+
local port2=8085
|
|
430
|
+
|
|
431
|
+
log_test_start "$test_name" "Validates statusline works with multiple CLASP instances"
|
|
432
|
+
|
|
433
|
+
# Pre-test cleanup
|
|
434
|
+
destroy_test_session "$session_name"
|
|
435
|
+
rm -f ~/.clasp/status/${port1}.json ~/.clasp/status/${port2}.json 2>/dev/null
|
|
436
|
+
|
|
437
|
+
# Check prerequisites
|
|
438
|
+
if [[ ! -x "$CLASP_DIR/bin/clasp" ]]; then
|
|
439
|
+
log_skip "$test_name" "CLASP binary not found"
|
|
440
|
+
return
|
|
441
|
+
fi
|
|
442
|
+
|
|
443
|
+
# Create test session
|
|
444
|
+
if ! create_test_session "$session_name"; then
|
|
445
|
+
log_fail "$test_name" "Could not create tmux session"
|
|
446
|
+
return
|
|
447
|
+
fi
|
|
448
|
+
|
|
449
|
+
# Step 1: Start first instance
|
|
450
|
+
log " Step 1: Starting first CLASP instance on port $port1..."
|
|
451
|
+
tmux send-keys -t "$session_name" "cd $CLASP_DIR && ./bin/clasp --port $port1 --proxy-only 2>&1 &" Enter
|
|
452
|
+
|
|
453
|
+
if ! wait_for_condition "[ -f ~/.clasp/status/${port1}.json ]" 10; then
|
|
454
|
+
log_fail "$test_name" "First instance did not start"
|
|
455
|
+
destroy_test_session "$session_name"
|
|
456
|
+
return
|
|
457
|
+
fi
|
|
458
|
+
|
|
459
|
+
# Step 2: Start second instance
|
|
460
|
+
log " Step 2: Starting second CLASP instance on port $port2..."
|
|
461
|
+
tmux send-keys -t "$session_name" "./bin/clasp --port $port2 --proxy-only 2>&1 &" Enter
|
|
462
|
+
|
|
463
|
+
if ! wait_for_condition "[ -f ~/.clasp/status/${port2}.json ]" 10; then
|
|
464
|
+
log_fail "$test_name" "Second instance did not start"
|
|
465
|
+
destroy_test_session "$session_name"
|
|
466
|
+
return
|
|
467
|
+
fi
|
|
468
|
+
|
|
469
|
+
# Step 3: Verify both status files exist and are distinct
|
|
470
|
+
log " Step 3: Verifying status files..."
|
|
471
|
+
local status1 status2 pid1 pid2
|
|
472
|
+
status1=$(cat ~/.clasp/status/${port1}.json 2>/dev/null)
|
|
473
|
+
status2=$(cat ~/.clasp/status/${port2}.json 2>/dev/null)
|
|
474
|
+
|
|
475
|
+
pid1=$(echo "$status1" | jq -r '.pid')
|
|
476
|
+
pid2=$(echo "$status2" | jq -r '.pid')
|
|
477
|
+
|
|
478
|
+
if [[ "$pid1" != "$pid2" ]]; then
|
|
479
|
+
log " ✓ Status files have distinct PIDs ($pid1 vs $pid2)"
|
|
480
|
+
else
|
|
481
|
+
log_fail "$test_name" "Status files have same PID"
|
|
482
|
+
destroy_test_session "$session_name"
|
|
483
|
+
return
|
|
484
|
+
fi
|
|
485
|
+
|
|
486
|
+
# Step 4: Test statusline for each instance
|
|
487
|
+
log " Step 4: Testing statusline for each instance..."
|
|
488
|
+
local out1 out2
|
|
489
|
+
out1=$(ANTHROPIC_BASE_URL="http://localhost:$port1" ~/.claude/clasp-statusline.sh '{}' 2>&1)
|
|
490
|
+
out2=$(ANTHROPIC_BASE_URL="http://localhost:$port2" ~/.claude/clasp-statusline.sh '{}' 2>&1)
|
|
491
|
+
|
|
492
|
+
if [[ "$out1" == *"[CLASP:$port1]"* ]] && [[ "$out2" == *"[CLASP:$port2]"* ]]; then
|
|
493
|
+
log " ✓ Statusline correctly identifies each instance"
|
|
494
|
+
log " Instance 1: $out1"
|
|
495
|
+
log " Instance 2: $out2"
|
|
496
|
+
log_pass "$test_name"
|
|
497
|
+
else
|
|
498
|
+
log_fail "$test_name" "Statusline did not correctly identify instances"
|
|
499
|
+
log " Instance 1 output: $out1"
|
|
500
|
+
log " Instance 2 output: $out2"
|
|
501
|
+
fi
|
|
502
|
+
|
|
503
|
+
# Cleanup
|
|
504
|
+
destroy_test_session "$session_name"
|
|
505
|
+
rm -f ~/.clasp/status/${port1}.json ~/.clasp/status/${port2}.json 2>/dev/null
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
#######################################
|
|
509
|
+
# Main Test Runner
|
|
510
|
+
#######################################
|
|
511
|
+
|
|
512
|
+
main() {
|
|
513
|
+
log_section "CLASP Test Suite"
|
|
514
|
+
log "Timestamp: $(date)"
|
|
515
|
+
log "Results file: $RESULTS_FILE"
|
|
516
|
+
log "CLASP directory: $CLASP_DIR"
|
|
517
|
+
|
|
518
|
+
# Initial cleanup
|
|
519
|
+
cleanup_test_sessions
|
|
520
|
+
|
|
521
|
+
# Ensure CLASP is built
|
|
522
|
+
if [[ ! -x "$CLASP_DIR/bin/clasp" ]]; then
|
|
523
|
+
log "${YELLOW}Building CLASP binary...${NC}"
|
|
524
|
+
(cd "$CLASP_DIR" && go build -o bin/clasp ./cmd/clasp)
|
|
525
|
+
fi
|
|
526
|
+
|
|
527
|
+
# Run tests
|
|
528
|
+
log_section "Running Tests"
|
|
529
|
+
|
|
530
|
+
test_statusline
|
|
531
|
+
log ""
|
|
532
|
+
|
|
533
|
+
test_proxy_lifecycle
|
|
534
|
+
log ""
|
|
535
|
+
|
|
536
|
+
test_multi_instance
|
|
537
|
+
log ""
|
|
538
|
+
|
|
539
|
+
test_tool_calling
|
|
540
|
+
log ""
|
|
541
|
+
|
|
542
|
+
# Final cleanup
|
|
543
|
+
cleanup_test_sessions
|
|
544
|
+
|
|
545
|
+
# Summary
|
|
546
|
+
log_section "Test Summary"
|
|
547
|
+
log "${GREEN}Passed: $TESTS_PASSED${NC}"
|
|
548
|
+
log "${RED}Failed: $TESTS_FAILED${NC}"
|
|
549
|
+
log "${YELLOW}Skipped: $TESTS_SKIPPED${NC}"
|
|
550
|
+
log ""
|
|
551
|
+
log "Results saved to: $RESULTS_FILE"
|
|
552
|
+
|
|
553
|
+
# Exit with failure if any tests failed
|
|
554
|
+
if [[ $TESTS_FAILED -gt 0 ]]; then
|
|
555
|
+
exit 1
|
|
556
|
+
fi
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
# Handle arguments
|
|
560
|
+
case "${1:-}" in
|
|
561
|
+
--statusline)
|
|
562
|
+
cleanup_test_sessions
|
|
563
|
+
test_statusline
|
|
564
|
+
cleanup_test_sessions
|
|
565
|
+
;;
|
|
566
|
+
--tools)
|
|
567
|
+
cleanup_test_sessions
|
|
568
|
+
test_tool_calling
|
|
569
|
+
cleanup_test_sessions
|
|
570
|
+
;;
|
|
571
|
+
--lifecycle)
|
|
572
|
+
cleanup_test_sessions
|
|
573
|
+
test_proxy_lifecycle
|
|
574
|
+
cleanup_test_sessions
|
|
575
|
+
;;
|
|
576
|
+
--multi)
|
|
577
|
+
cleanup_test_sessions
|
|
578
|
+
test_multi_instance
|
|
579
|
+
cleanup_test_sessions
|
|
580
|
+
;;
|
|
581
|
+
--help|-h)
|
|
582
|
+
echo "CLASP Test Suite"
|
|
583
|
+
echo ""
|
|
584
|
+
echo "Usage: $0 [option]"
|
|
585
|
+
echo ""
|
|
586
|
+
echo "Options:"
|
|
587
|
+
echo " (none) Run all tests"
|
|
588
|
+
echo " --statusline Run only statusline test"
|
|
589
|
+
echo " --tools Run only tool schema test"
|
|
590
|
+
echo " --lifecycle Run only proxy lifecycle test"
|
|
591
|
+
echo " --multi Run only multi-instance test"
|
|
592
|
+
echo " --help Show this help"
|
|
593
|
+
;;
|
|
594
|
+
*)
|
|
595
|
+
main
|
|
596
|
+
;;
|
|
597
|
+
esac
|