#!/usr/bin/env bash # test-curl.sh — manual smoke test for vps-health-api endpoints. # # Spins up the server against a temporary REPORTS_DIR populated with # scan/run-report fixtures, then runs curl against each endpoint and # checks status codes + payload shape. No test runner installed — this # script is the authoritative regression suite for the GET /reports/scans # endpoint until vitest/jest is added. # # Usage : # bash test-curl.sh # # Exit 0 if all 10 cases pass, exit 1 on first failure (fail-fast). set -euo pipefail BASE_URL="${BASE_URL:-http://localhost:3099}" TOKEN="${TOKEN:-test-token-123}" TMP_DIR="$(mktemp -d -t vps-health-api.XXXXXX)" trap 'rm -rf "$TMP_DIR"; kill "$SERVER_PID" 2>/dev/null || true' EXIT # Fixtures : # - 3 scan reports on 2026-05-07 (booking, simpl-liste, maximus) # - 1 defenseur-auto run report on 2026-05-07 (must be filtered out) # - 1 booking scan report on 2026-05-06 (must be excluded by date filter) mkdir -p "$TMP_DIR/reports" cat > "$TMP_DIR/reports/defenseur-booking_2026-05-07T05-30-11-249Z.json" <<'JSON' { "agent": "defenseur-booking", "timestamp": "2026-05-07T05:30:11.249Z", "project": "la-suite-booking", "checksRun": 16, "checksPassed": 14, "findings": [] } JSON cat > "$TMP_DIR/reports/defenseur-simpl-liste_2026-05-07T05-32-04-512Z.json" <<'JSON' { "agent": "defenseur-simpl-liste", "timestamp": "2026-05-07T05:32:04.512Z", "project": "simpl-liste", "checksRun": 12, "checksPassed": 11, "findings": [] } JSON cat > "$TMP_DIR/reports/defenseur-maximus_2026-05-07T05-00-12-100Z.json" <<'JSON' { "agent": "defenseur-maximus", "timestamp": "2026-05-07T05:00:12.100Z", "project": "la-compagnie-maximus", "checksRun": 8, "checksPassed": 8, "findings": [] } JSON cat > "$TMP_DIR/reports/defenseur-auto_2026-05-07.json" <<'JSON' { "agent": "defenseur-auto", "timestamp": "2026-05-07T07:00:00.000Z", "status": "ok", "actions": [], "skipped": [], "totalCostUsd": 0 } JSON cat > "$TMP_DIR/reports/defenseur-booking_2026-05-06T05-30-00-000Z.json" <<'JSON' { "agent": "defenseur-booking", "timestamp": "2026-05-06T05:30:00.000Z", "project": "la-suite-booking", "checksRun": 16, "checksPassed": 16, "findings": [] } JSON # Boot the server with the temp REPORTS_DIR. PORT=3099 \ HEALTH_TOKEN="$TOKEN" \ REPORTS_DIR="$TMP_DIR/reports" \ LOGTO_HEALTH_URL="http://127.0.0.1:1/never" \ node "$(dirname "$0")/index.js" >/dev/null 2>&1 & SERVER_PID=$! # Wait for the server to be ready. for _ in {1..50}; do if curl -s -o /dev/null "$BASE_URL/health" 2>/dev/null; then break; fi sleep 0.1 done PASS=0 FAIL=0 fail() { echo "FAIL: $1" FAIL=$((FAIL+1)) } pass() { echo "PASS: $1" PASS=$((PASS+1)) } # Case 1 : no auth -> 401 code=$(curl -s -o /dev/null -w '%{http_code}' "$BASE_URL/reports/scans?date=2026-05-07") [[ "$code" == "401" ]] && pass "no-auth -> 401" || fail "no-auth -> got $code" # Case 2 : wrong token -> 401 code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer wrong" "$BASE_URL/reports/scans?date=2026-05-07") [[ "$code" == "401" ]] && pass "wrong-token -> 401" || fail "wrong-token -> got $code" # Case 3 : valid token + date 2026-05-07 -> 200, count=3, sorted asc, no auto report body=$(curl -s -H "Authorization: Bearer $TOKEN" \ "$BASE_URL/reports/scans?date=2026-05-07") count=$(echo "$body" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{console.log(JSON.parse(s).count);});') agents=$(echo "$body" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{console.log(JSON.parse(s).reports.map(r=>r.agent).join(","));});') [[ "$count" == "3" ]] && pass "valid date 2026-05-07 -> count=3" || fail "valid date 2026-05-07 -> count=$count" [[ "$agents" == "defenseur-maximus,defenseur-booking,defenseur-simpl-liste" ]] \ && pass "sort by timestamp asc + auto filtered" \ || fail "sort/filter mismatch -> $agents" # Case 4 : invalid date format -> 400 code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer $TOKEN" "$BASE_URL/reports/scans?date=hello") [[ "$code" == "400" ]] && pass "invalid date -> 400" || fail "invalid date -> got $code" # Case 5 : missing date param -> 400 code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer $TOKEN" "$BASE_URL/reports/scans") [[ "$code" == "400" ]] && pass "missing date -> 400" || fail "missing date -> got $code" # Case 6 : valid token + unknown date (no fixtures) -> 200 count=0 body=$(curl -s -H "Authorization: Bearer $TOKEN" \ "$BASE_URL/reports/scans?date=2025-01-01") count=$(echo "$body" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{console.log(JSON.parse(s).count);});') [[ "$count" == "0" ]] && pass "unknown date -> count=0" || fail "unknown date -> count=$count" # Case 7 : valid token + date 2026-05-06 -> 200 count=1 body=$(curl -s -H "Authorization: Bearer $TOKEN" \ "$BASE_URL/reports/scans?date=2026-05-06") count=$(echo "$body" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{console.log(JSON.parse(s).count);});') [[ "$count" == "1" ]] && pass "date 2026-05-06 -> count=1" || fail "date 2026-05-06 -> count=$count" # Case 8 : path traversal -> 400 (regex blocks) code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer $TOKEN" \ "$BASE_URL/reports/scans?date=../../../etc/passwd") [[ "$code" == "400" ]] && pass "path traversal -> 400" || fail "path traversal -> got $code" # Case 9 : POST -> 404 (GET-only) code=$(curl -s -o /dev/null -w '%{http_code}' -X POST \ -H "Authorization: Bearer $TOKEN" \ "$BASE_URL/reports/scans?date=2026-05-07") [[ "$code" == "404" ]] && pass "POST -> 404" || fail "POST -> got $code" # Case 10 : wrong path -> 404 code=$(curl -s -o /dev/null -w '%{http_code}' \ -H "Authorization: Bearer $TOKEN" "$BASE_URL/reports/nope") [[ "$code" == "404" ]] && pass "wrong path -> 404" || fail "wrong path -> got $code" echo echo "=== Results: $PASS passed, $FAIL failed ===" [[ "$FAIL" == "0" ]] || exit 1