fix(deploy): test-api.sh — fix curl -f flag swallowing 4xx, fgrep for literal [, correct /auth/me path

This commit is contained in:
Zac Gaetano 2026-05-20 13:52:59 -04:00
parent 4dd377e28d
commit 115c7340ee

View file

@ -23,15 +23,15 @@ fail() { FAIL=$((FAIL+1)); echo -e " ${RED}FAIL${NC} $1 ${RED}← $2${NC}"
skip() { SKIP=$((SKIP+1)); echo -e " ${YEL}SKIP${NC} $1 ${YEL}($2)${NC}"; } skip() { SKIP=$((SKIP+1)); echo -e " ${YEL}SKIP${NC} $1 ${YEL}($2)${NC}"; }
header() { echo -e "\n${BLD}$1${NC}"; } header() { echo -e "\n${BLD}$1${NC}"; }
# Auth headers array (works for both GNU and BSD curl)
AUTH_ARGS=() AUTH_ARGS=()
[[ -n "$TOKEN" ]] && AUTH_ARGS+=(-H "Authorization: Bearer $TOKEN") [[ -n "$TOKEN" ]] && AUTH_ARGS+=(-H "Authorization: Bearer $TOKEN")
# GET request — check HTTP status code # GET — check HTTP status code (no -f so 4xx/5xx are visible)
check_status() { check_status() {
local label="$1" path="$2" want="$3" local label="$1" path="$2" want="$3"
local got local got
got=$(curl -sf -o /dev/null -w "%{http_code}" "${AUTH_ARGS[@]}" "$BASE$path" 2>/dev/null) || got="000" got=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH_ARGS[@]}" "$BASE$path" 2>/dev/null)
[[ -z "$got" ]] && got="000"
if [[ "$got" == "$want" ]]; then if [[ "$got" == "$want" ]]; then
pass "$label [HTTP $got]" pass "$label [HTTP $got]"
else else
@ -39,27 +39,28 @@ check_status() {
fi fi
} }
# GET request — check response body contains a string # GET — check response body contains literal string (fgrep avoids regex interpretation)
check_body() { check_body() {
local label="$1" path="$2" needle="$3" local label="$1" path="$2" needle="$3"
local body local body
body=$(curl -sf "${AUTH_ARGS[@]}" "$BASE$path" 2>/dev/null) || { fail "$label" "request failed"; return; } body=$(curl -s "${AUTH_ARGS[@]}" "$BASE$path" 2>/dev/null) || { fail "$label" "request failed"; return; }
if echo "$body" | grep -q "$needle"; then if echo "$body" | grep -qF "$needle"; then
pass "$label" pass "$label"
else else
fail "$label" "'$needle' not in response" fail "$label" "'$needle' not in response"
fi fi
} }
# POST request — check HTTP status # POST — check HTTP status code
check_post() { check_post() {
local label="$1" path="$2" data="$3" want="$4" local label="$1" path="$2" data="$3" want="$4"
local got local got
got=$(curl -sf -o /dev/null -w "%{http_code}" \ got=$(curl -s -o /dev/null -w "%{http_code}" \
"${AUTH_ARGS[@]}" \ "${AUTH_ARGS[@]}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-X POST -d "$data" \ -X POST -d "$data" \
"$BASE$path" 2>/dev/null) || got="000" "$BASE$path" 2>/dev/null)
[[ -z "$got" ]] && got="000"
if [[ "$got" == "$want" ]]; then if [[ "$got" == "$want" ]]; then
pass "$label [HTTP $got]" pass "$label [HTTP $got]"
else else
@ -76,85 +77,96 @@ echo ""
# ── Connectivity ───────────────────────────────────────────────────────────── # ── Connectivity ─────────────────────────────────────────────────────────────
header "Connectivity" header "Connectivity"
if curl -sf -o /dev/null "$BASE/api/v1/auth/whoami" 2>/dev/null || \ CONNECT=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/health" 2>/dev/null)
curl -sf -o /dev/null "$BASE/api/v1/cluster" 2>/dev/null; then if [[ "$CONNECT" == "200" ]]; then
pass "API server reachable at $BASE" pass "API server reachable [/health → 200]"
else else
fail "API server reachable" "cannot connect to $BASE" fail "API server reachable [HTTP $CONNECT]" "cannot reach $BASE"
echo -e "\n ${RED}Cannot reach the server — aborting.${NC}" echo -e "\n ${RED}Cannot reach the server — aborting.${NC}"
exit 1 exit 1
fi fi
# ── Auth ───────────────────────────────────────────────────────────────────── # ── Auth ─────────────────────────────────────────────────────────────────────
header "Auth" header "Auth"
check_status "GET /auth/whoami" "/api/v1/auth/whoami" 200 check_status "GET /auth/me" "/api/v1/auth/me" 200
check_body "GET /auth/me returns username" "/api/v1/auth/me" '"username"'
check_status "POST /auth/login (bad creds → 400)" "/api/v1/auth/login" 400
# ── Assets ─────────────────────────────────────────────────────────────────── # ── Assets ───────────────────────────────────────────────────────────────────
header "Assets" header "Assets"
check_status "GET /assets" "/api/v1/assets" 200 check_status "GET /assets" "/api/v1/assets" 200
check_status "GET /assets 404 on bogus" "/api/v1/assets/00000000-0000-0000-0000-000000000000" 404 check_body "GET /assets returns assets key" "/api/v1/assets" '"assets"'
check_status "GET /assets bogus id → 404" "/api/v1/assets/00000000-0000-0000-0000-000000000000" 404
# ── Projects ───────────────────────────────────────────────────────────────── # ── Projects ─────────────────────────────────────────────────────────────────
header "Projects" header "Projects"
check_status "GET /projects" "/api/v1/projects" 200 check_status "GET /projects" "/api/v1/projects" 200
check_body "GET /projects returns []" "/api/v1/projects" "[" check_body "GET /projects returns array" "/api/v1/projects" '['
# ── Bins ─────────────────────────────────────────────────────────────────────
header "Bins"
check_status "GET /bins" "/api/v1/bins" 200
# ── Jobs ───────────────────────────────────────────────────────────────────── # ── Jobs ─────────────────────────────────────────────────────────────────────
header "Jobs" header "Jobs"
check_status "GET /jobs" "/api/v1/jobs" 200 check_status "GET /jobs" "/api/v1/jobs" 200
check_body "GET /jobs returns array" "/api/v1/jobs" "[" check_body "GET /jobs returns array" "/api/v1/jobs" '['
# ── Recorders ──────────────────────────────────────────────────────────────── # ── Recorders ────────────────────────────────────────────────────────────────
header "Recorders" header "Recorders"
check_status "GET /recorders" "/api/v1/recorders" 200 check_status "GET /recorders" "/api/v1/recorders" 200
# ── Sequences (Editor) ─────────────────────────────────────────────────────── # ── Sequences (Editor) ───────────────────────────────────────────────────────
header "Sequences" header "Sequences"
check_status "GET /sequences" "/api/v1/sequences" 200 check_status "GET /sequences" "/api/v1/sequences" 200
# ── Cluster ────────────────────────────────────────────────────────────────── # ── Cluster ──────────────────────────────────────────────────────────────────
header "Cluster" header "Cluster"
check_status "GET /cluster" "/api/v1/cluster" 200 check_status "GET /cluster" "/api/v1/cluster" 200
check_body "GET /cluster returns []" "/api/v1/cluster" "[" check_body "GET /cluster returns array" "/api/v1/cluster" '['
# Heartbeat test — registers a temporary smoke-test node # Heartbeat: register a temporary smoke-test node, verify it appears, remove it
TEST_HOST="smoke-test-$(date +%s)" TEST_HOST="smoke-test-$(date +%s)"
check_post "POST /cluster/heartbeat" "/api/v1/cluster/heartbeat" \ check_post "POST /cluster/heartbeat" "/api/v1/cluster/heartbeat" \
"{\"hostname\":\"$TEST_HOST\",\"role\":\"smoketest\",\"cpu_usage\":0,\"mem_used_mb\":512,\"mem_total_mb\":4096}" \ "{\"hostname\":\"$TEST_HOST\",\"role\":\"smoketest\",\"cpu_usage\":0,\"mem_used_mb\":512,\"mem_total_mb\":4096}" \
200 200
# Verify the node appeared # Find the node we just created (match by hostname)
NODE_ID=$(curl -sf "${AUTH_ARGS[@]}" "$BASE/api/v1/cluster" 2>/dev/null \ NODE_ID=$(curl -s "${AUTH_ARGS[@]}" "$BASE/api/v1/cluster" 2>/dev/null \
| grep -o '"id":"[^"]*"' | head -1 | grep -o '[0-9a-f-]\{36\}' || true) | grep -o "\"id\":\"[^\"]*\"" | head -1 | grep -o '[0-9a-f-]\{36\}' || true)
if [[ -n "$NODE_ID" ]]; then if [[ -n "$NODE_ID" ]]; then
pass "Cluster node visible in registry" pass "Cluster node visible in registry"
# Clean up — deregister the smoke node DEL=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH_ARGS[@]}" \
DEL_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" \ -X DELETE "$BASE/api/v1/cluster/$NODE_ID" 2>/dev/null)
"${AUTH_ARGS[@]}" -X DELETE "$BASE/api/v1/cluster/$NODE_ID" 2>/dev/null) || DEL_STATUS="000" [[ "$DEL" == "200" ]] && pass "DELETE /cluster/:id (cleanup) [HTTP $DEL]" \
[[ "$DEL_STATUS" == "200" ]] && pass "DELETE /cluster/:id (cleanup)" \ || fail "DELETE /cluster/:id (cleanup)" "HTTP $DEL"
|| fail "DELETE /cluster/:id (cleanup)" "HTTP $DEL_STATUS"
else else
skip "Cluster node visible in registry" "could not parse node id" skip "Cluster node visible in registry" "could not parse node id from response"
fi fi
# ── System / Containers ─────────────────────────────────────────────────────── # ── System / Containers ───────────────────────────────────────────────────────
header "System" header "System"
check_status "GET /system/containers" "/api/v1/system/containers" 200 check_status "GET /system/containers" "/api/v1/system/containers" 200
check_body "Containers returns array" "/api/v1/system/containers" "[" check_body "Containers returns array" "/api/v1/system/containers" '['
# ── Capture ────────────────────────────────────────────────────────────────── # ── Capture (proxies to capture service) ─────────────────────────────────────
header "Capture" header "Capture"
check_status "GET /capture/status" "/api/v1/capture/status" 200 # /capture/status proxies to the capture container; 200 = service up, 5xx = capture unreachable
CAPTURE_CODE=$(curl -s -o /dev/null -w "%{http_code}" "${AUTH_ARGS[@]}" \
"$BASE/api/v1/capture/status" 2>/dev/null)
if [[ "$CAPTURE_CODE" == "200" ]]; then
pass "GET /capture/status [HTTP 200]"
elif [[ "$CAPTURE_CODE" == "500" || "$CAPTURE_CODE" == "502" || "$CAPTURE_CODE" == "503" ]]; then
skip "GET /capture/status [HTTP $CAPTURE_CODE]" "capture service unreachable (container down?)"
else
fail "GET /capture/status [HTTP $CAPTURE_CODE]" "expected 200 or 5xx proxy error"
fi
# ── Users / Tokens (admin) ─────────────────────────────────────────────────── # ── Settings ─────────────────────────────────────────────────────────────────
header "Settings"
check_status "GET /settings" "/api/v1/settings" 200
# ── Users / Tokens ───────────────────────────────────────────────────────────
header "Users / Tokens" header "Users / Tokens"
check_status "GET /users" "/api/v1/users" 200 check_status "GET /users" "/api/v1/users" 200
check_status "GET /tokens" "/api/v1/tokens" 200 check_status "GET /tokens" "/api/v1/tokens" 200
# ── Summary ─────────────────────────────────────────────────────────────────── # ── Summary ───────────────────────────────────────────────────────────────────
TOTAL=$((PASS + FAIL + SKIP)) TOTAL=$((PASS + FAIL + SKIP))
@ -163,7 +175,7 @@ echo -e "${BLD}Results:${NC} ${GRN}${PASS} passed${NC} / ${RED}${FAIL} failed
echo "" echo ""
if [[ $FAIL -gt 0 ]]; then if [[ $FAIL -gt 0 ]]; then
echo -e "${RED}Some tests failed. Check the output above.${NC}" echo -e "${RED}Some tests failed — check output above.${NC}"
exit 1 exit 1
else else
echo -e "${GRN}All tests passed.${NC}" echo -e "${GRN}All tests passed.${NC}"