Extracted from ardenone-cluster/containers/zai-proxy and ardenone-cluster/containers/zai-proxy-dashboard. - proxy/: OpenAI-compatible ZAI reverse proxy (Go, v1.10.0) - Token counting, rate limiting, Prometheus metrics, canary support - dashboard/: Metrics dashboard backend + React frontend (Go, v1.0.0) - Prometheus collector, SQLite storage, SSE live updates - docs/: Operational notes, research, and plan subdirs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
280 lines
9.2 KiB
Bash
Executable file
280 lines
9.2 KiB
Bash
Executable file
#!/bin/bash
|
|
# Canary Integration Test Script for ZAI Proxy Token Counting
|
|
# This script runs the full integration test suite against the canary deployment
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
CANARY_URL="${CANARY_URL:-http://zai-proxy-canary.devpod.svc.cluster.local:8080}"
|
|
PROD_URL="${PROD_URL:-http://zai-proxy.devpod.svc.cluster.local:8080}"
|
|
NAMESPACE="${NAMESPACE:-devpod}"
|
|
|
|
# Get API key from secret
|
|
echo "Getting API key from secret..."
|
|
API_KEY=$(kubectl get secret -n "$NAMESPACE" zai-api-key -o jsonpath='{.data.key}' 2>/dev/null | base64 -d || echo "")
|
|
|
|
if [ -z "$API_KEY" ]; then
|
|
echo -e "${RED}ERROR: Could not get API key from secret${NC}"
|
|
echo "Please set API_KEY environment variable or ensure secret exists"
|
|
exit 1
|
|
fi
|
|
|
|
# Test counter
|
|
TOTAL_TESTS=0
|
|
PASSED_TESTS=0
|
|
FAILED_TESTS=0
|
|
|
|
# Helper functions
|
|
print_header() {
|
|
echo ""
|
|
echo "========================================"
|
|
echo "$1"
|
|
echo "========================================"
|
|
}
|
|
|
|
print_test() {
|
|
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
|
echo -e "\n${YELLOW}[Test $TOTAL_TESTS]${NC} $1"
|
|
}
|
|
|
|
print_pass() {
|
|
echo -e "${GREEN}✓ PASS${NC}: $1"
|
|
PASSED_TESTS=$((PASSED_TESTS + 1))
|
|
}
|
|
|
|
print_fail() {
|
|
echo -e "${RED}✗ FAIL${NC}: $1"
|
|
FAILED_TESTS=$((FAILED_TESTS + 1))
|
|
}
|
|
|
|
# Pre-flight checks
|
|
print_header "Pre-flight Checks"
|
|
|
|
echo -n "Checking canary deployment... "
|
|
if kubectl get deployment -n "$NAMESPACE" zai-proxy-canary >/dev/null 2>&1; then
|
|
print_pass "Canary deployment exists"
|
|
else
|
|
print_fail "Canary deployment NOT found. Deploy canary first."
|
|
exit 1
|
|
fi
|
|
|
|
echo -n "Checking canary service... "
|
|
if kubectl get service -n "$NAMESPACE" zai-proxy-canary >/dev/null 2>&1; then
|
|
print_pass "Canary service exists"
|
|
else
|
|
print_fail "Canary service NOT found"
|
|
exit 1
|
|
fi
|
|
|
|
echo -n "Checking production pods... "
|
|
PROD_PODS=$(kubectl get pods -n "$NAMESPACE" -l app=zai-proxy --no-headers 2>/dev/null | wc -l)
|
|
if [ "$PROD_PODS" -ge 5 ]; then
|
|
print_pass "Production has $PROD_PODS pods running"
|
|
else
|
|
print_fail "Production has only $PROD_PODS pods (expected >=5)"
|
|
fi
|
|
|
|
# Test 1: Token Counting Validation
|
|
print_header "Test 1: Token Counting Validation"
|
|
|
|
print_test "Sending basic request to canary..."
|
|
RESPONSE=$(curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-H "anthropic-version: 2023-06-01" \
|
|
-d '{
|
|
"model": "glm-4",
|
|
"max_tokens": 100,
|
|
"messages": [{"role": "user", "content": "Hello, how are you?"}]
|
|
}' 2>&1)
|
|
|
|
if echo "$RESPONSE" | jq -e '.content' >/dev/null 2>&1; then
|
|
print_pass "Canary responds with valid JSON"
|
|
else
|
|
print_fail "Canary response invalid: $RESPONSE"
|
|
fi
|
|
|
|
print_test "Checking Prometheus metrics for token counts..."
|
|
METRICS=$(curl -s "$CANARY_URL/metrics" 2>/dev/null || echo "")
|
|
if echo "$METRICS" | grep -q "zai_proxy_tokens_total"; then
|
|
TOKEN_COUNT=$(echo "$METRICS" | grep "zai_proxy_tokens_total.*direction=\"input\"" | awk '{print $2}')
|
|
if [ "$TOKEN_COUNT" -gt 0 ]; then
|
|
print_pass "Token counting active: $TOKEN_COUNT input tokens counted"
|
|
else
|
|
print_fail "Token count is 0"
|
|
fi
|
|
else
|
|
print_fail "Token metrics not found"
|
|
fi
|
|
|
|
print_test "Verifying production has NO token counting..."
|
|
PROD_METRICS=$(curl -s "$PROD_URL/metrics" 2>/dev/null || echo "")
|
|
if echo "$PROD_METRICS" | grep -q "zai_proxy_tokens_total"; then
|
|
PROD_TOKEN_COUNT=$(echo "$PROD_METRICS" | grep "zai_proxy_tokens_total.*direction=\"input\"" | awk '{print $2}')
|
|
if [ "$PROD_TOKEN_COUNT" = "0" ] || [ -z "$PROD_TOKEN_COUNT" ]; then
|
|
print_pass "Production has no token counting (as expected)"
|
|
else
|
|
print_fail "Production unexpectedly has token counting: $PROD_TOKEN_COUNT"
|
|
fi
|
|
else
|
|
print_pass "Production has no token metrics (as expected)"
|
|
fi
|
|
|
|
# Test 2: Format Comparison
|
|
print_header "Test 2: Format Comparison Tests"
|
|
|
|
print_test "Comparing response formats..."
|
|
PROD_RESPONSE=$(curl -s -X POST "$PROD_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":50,"messages":[{"role":"user","content":"Say hello"}]}' 2>/dev/null)
|
|
|
|
CANARY_RESPONSE=$(curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":50,"messages":[{"role":"user","content":"Say hello"}]}' 2>/dev/null)
|
|
|
|
# Check both have required fields
|
|
PROD_HAS_ID=$(echo "$PROD_RESPONSE" | jq -e '.id' >/dev/null 2>&1 && echo "yes" || echo "no")
|
|
CANARY_HAS_ID=$(echo "$CANARY_RESPONSE" | jq -e '.id' >/dev/null 2>&1 && echo "yes" || echo "no")
|
|
PROD_HAS_CONTENT=$(echo "$PROD_RESPONSE" | jq -e '.content' >/dev/null 2>&1 && echo "yes" || echo "no")
|
|
CANARY_HAS_CONTENT=$(echo "$CANARY_RESPONSE" | jq -e '.content' >/dev/null 2>&1 && echo "yes" || echo "no")
|
|
|
|
if [ "$PROD_HAS_ID" = "yes" ] && [ "$CANARY_HAS_ID" = "yes" ] && \
|
|
[ "$PROD_HAS_CONTENT" = "yes" ] && [ "$CANARY_HAS_CONTENT" = "yes" ]; then
|
|
print_pass "Both responses have identical structure"
|
|
else
|
|
print_fail "Response structure mismatch"
|
|
fi
|
|
|
|
# Test 3: Performance Benchmarks
|
|
print_header "Test 3: Performance Benchmarks"
|
|
|
|
print_test "Measuring production latency (10 samples)..."
|
|
PROD_TOTAL=0
|
|
for i in {1..10}; do
|
|
START=$(date +%s%3N)
|
|
curl -s -X POST "$PROD_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":20,"messages":[{"role":"user","content":"Hi"}]}' >/dev/null 2>&1
|
|
END=$(date +%s%3N)
|
|
PROD_TOTAL=$((PROD_TOTAL + END - START))
|
|
done
|
|
PROD_AVG=$((PROD_TOTAL / 10))
|
|
echo "Production average: ${PROD_AVG}ms"
|
|
|
|
print_test "Measuring canary latency (10 samples)..."
|
|
CANARY_TOTAL=0
|
|
for i in {1..10}; do
|
|
START=$(date +%s%3N)
|
|
curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":20,"messages":[{"role":"user","content":"Hi"}]}' >/dev/null 2>&1
|
|
END=$(date +%s%3N)
|
|
CANARY_TOTAL=$((CANARY_TOTAL + END - START))
|
|
done
|
|
CANARY_AVG=$((CANARY_TOTAL / 10))
|
|
echo "Canary average: ${CANARY_AVG}ms"
|
|
|
|
OVERHEAD=$((CANARY_AVG - PROD_AVG))
|
|
if [ $OVERHEAD -lt 50 ]; then
|
|
print_pass "Canary overhead: ${OVERHEAD}ms (within 50ms threshold)"
|
|
else
|
|
print_fail "Canary overhead: ${OVERHEAD}ms (exceeds 50ms threshold)"
|
|
fi
|
|
|
|
# Test 4: Streaming Response Tests
|
|
print_header "Test 4: Streaming Response Tests"
|
|
|
|
print_test "Testing streaming responses..."
|
|
STREAM_RESPONSE=$(curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":50,"stream":true,"messages":[{"role":"user","content":"Count to 3"}]}' 2>/dev/null)
|
|
|
|
if echo "$STREAM_RESPONSE" | grep -q "data:"; then
|
|
print_pass "Streaming response format correct"
|
|
else
|
|
print_fail "Streaming response missing SSE format"
|
|
fi
|
|
|
|
# Test 5: Load Testing
|
|
print_header "Test 5: Load Testing (Concurrent Requests)"
|
|
|
|
print_test "Running 20 concurrent requests..."
|
|
FAILURES=0
|
|
for i in {1..20}; do
|
|
(
|
|
curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":20,"messages":[{"role":"user","content":"Test"}]}' >/dev/null 2>&1
|
|
) &
|
|
done
|
|
wait
|
|
|
|
if [ $FAILURES -eq 0 ]; then
|
|
print_pass "All concurrent requests completed"
|
|
else
|
|
print_fail "$FAILURES requests failed"
|
|
fi
|
|
|
|
# Test 6: Edge Cases
|
|
print_header "Test 6: Edge Cases"
|
|
|
|
print_test "Testing empty message..."
|
|
EMPTY_RESPONSE=$(curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":20,"messages":[{"role":"user","content":""}]}' 2>/dev/null)
|
|
if echo "$EMPTY_RESPONSE" | jq -e '.content' >/dev/null 2>&1; then
|
|
print_pass "Empty message handled"
|
|
else
|
|
print_fail "Empty message caused error"
|
|
fi
|
|
|
|
print_test "Testing multi-turn conversation..."
|
|
MULTI_RESPONSE=$(curl -s -X POST "$CANARY_URL/v1/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: $API_KEY" \
|
|
-d '{"model":"glm-4","max_tokens":50,"messages":[{"role":"user","content":"What is 2+2?"},{"role":"assistant","content":"4"},{"role":"user","content":"And 3+3?"}]}' 2>/dev/null)
|
|
if echo "$MULTI_RESPONSE" | jq -e '.content' >/dev/null 2>&1; then
|
|
print_pass "Multi-turn conversation handled"
|
|
else
|
|
print_fail "Multi-turn conversation failed"
|
|
fi
|
|
|
|
# Test 7: Production Isolation
|
|
print_header "Test 7: Production Isolation Verification"
|
|
|
|
print_test "Verifying production unaffected..."
|
|
FINAL_PROD_PODS=$(kubectl get pods -n "$NAMESPACE" -l app=zai-proxy --no-headers 2>/dev/null | wc -l)
|
|
if [ "$FINAL_PROD_PODS" = "$PROD_PODS" ]; then
|
|
print_pass "Production pods stable ($FINAL_PROD_PODS running)"
|
|
else
|
|
print_fail "Production pods changed ($PROD_PODS -> $FINAL_PROD_PODS)"
|
|
fi
|
|
|
|
# Final Summary
|
|
print_header "Test Summary"
|
|
echo ""
|
|
echo "Total Tests: $TOTAL_TESTS"
|
|
echo -e "${GREEN}Passed: $PASSED_TESTS${NC}"
|
|
echo -e "${RED}Failed: $FAILED_TESTS${NC}"
|
|
echo ""
|
|
|
|
if [ $FAILED_TESTS -eq 0 ]; then
|
|
echo -e "${GREEN}ALL TESTS PASSED${NC}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}SOME TESTS FAILED${NC}"
|
|
exit 1
|
|
fi
|