2026-03-01 04:08:53 +00:00
|
|
|
#!/bin/bash
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
# Smoke Test Script for dd0c Docker Compose Stack
|
2026-03-01 04:08:53 +00:00
|
|
|
# Tests health endpoints, tenant creation, and key API endpoints for all services
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
set -euo pipefail
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
# Colors
|
|
|
|
|
RED='\033[0;31m'
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
YELLOW='\033[1;33m'
|
2026-03-01 22:40:29 +00:00
|
|
|
NC='\033[0m'
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
# Configuration
|
|
|
|
|
BASE_URL="${BASE_URL:-localhost}"
|
|
|
|
|
PROTOCOL="${PROTOCOL:-http}"
|
2026-03-01 22:40:29 +00:00
|
|
|
TS=$(date +%s)
|
|
|
|
|
|
|
|
|
|
# Port mapping (matches docker-compose.yml)
|
|
|
|
|
PORT_DRIFT=3002
|
|
|
|
|
PORT_ALERT=3003
|
|
|
|
|
PORT_PORTAL=3004
|
|
|
|
|
PORT_RUN=3006
|
|
|
|
|
PORT_COST=3007
|
|
|
|
|
PORT_ROUTE_API=3001
|
|
|
|
|
PORT_ROUTE_PROXY=8080
|
|
|
|
|
PORT_PG=5433
|
|
|
|
|
PORT_REDIS=6379
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
# Counters
|
2026-03-01 22:40:29 +00:00
|
|
|
TOTAL=0
|
|
|
|
|
PASSED=0
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
show_help() {
|
|
|
|
|
cat << EOF
|
2026-03-01 22:40:29 +00:00
|
|
|
dd0c Smoke Test
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
Usage: $0 [OPTIONS]
|
|
|
|
|
|
|
|
|
|
Options:
|
2026-03-01 22:40:29 +00:00
|
|
|
--help Show this help
|
2026-03-01 04:08:53 +00:00
|
|
|
--base-url URL Set base URL (default: localhost)
|
|
|
|
|
--protocol PROTO Set protocol (default: http)
|
2026-03-01 22:40:29 +00:00
|
|
|
--skip-rust Skip Rust service checks (P1 route)
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
Examples:
|
2026-03-01 22:40:29 +00:00
|
|
|
$0 --base-url 192.168.86.11
|
|
|
|
|
BASE_URL=192.168.86.11 $0 --skip-rust
|
2026-03-01 04:08:53 +00:00
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
SKIP_RUST=false
|
2026-03-01 04:08:53 +00:00
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
|
|
case $1 in
|
2026-03-01 22:40:29 +00:00
|
|
|
--help) show_help; exit 0 ;;
|
|
|
|
|
--base-url) BASE_URL="$2"; shift 2 ;;
|
|
|
|
|
--protocol) PROTOCOL="$2"; shift 2 ;;
|
|
|
|
|
--skip-rust) SKIP_RUST=true; shift ;;
|
|
|
|
|
*) echo "Unknown: $1"; show_help; exit 1 ;;
|
2026-03-01 04:08:53 +00:00
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
check() {
|
|
|
|
|
local name=$1 status=$2
|
|
|
|
|
((TOTAL++)) || true
|
2026-03-01 04:08:53 +00:00
|
|
|
if [ "$status" = "pass" ]; then
|
2026-03-01 22:40:29 +00:00
|
|
|
echo -e " ${GREEN}✓${NC} $name" >&2
|
|
|
|
|
((PASSED++)) || true
|
2026-03-01 04:08:53 +00:00
|
|
|
else
|
2026-03-01 22:40:29 +00:00
|
|
|
echo -e " ${RED}✗${NC} $name" >&2
|
2026-03-01 04:08:53 +00:00
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
url() { echo "${PROTOCOL}://${BASE_URL}:$1$2"; }
|
|
|
|
|
|
|
|
|
|
signup() {
|
|
|
|
|
local port=$1 name=$2
|
|
|
|
|
local resp
|
|
|
|
|
resp=$(curl -s -X POST "$(url "$port" /api/v1/auth/signup)" \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-d "{\"email\":\"smoke-${TS}@dd0c.dev\",\"password\":\"smoketest123!\",\"tenant_name\":\"Smoke ${TS}\"}" 2>/dev/null || echo "")
|
|
|
|
|
local token
|
|
|
|
|
token=$(echo "$resp" | grep -o '"token":"[^"]*' | cut -d'"' -f4 || echo "")
|
|
|
|
|
if [ -n "$token" ]; then
|
|
|
|
|
check "Signup: $name (:$port)" "pass"
|
|
|
|
|
echo "$token"
|
2026-03-01 04:08:53 +00:00
|
|
|
else
|
2026-03-01 22:40:29 +00:00
|
|
|
check "Signup: $name (:$port) — $(echo "$resp" | head -c 80)" "fail"
|
|
|
|
|
echo ""
|
2026-03-01 04:08:53 +00:00
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
login() {
|
|
|
|
|
local port=$1 name=$2
|
|
|
|
|
local resp
|
|
|
|
|
resp=$(curl -s -X POST "$(url "$port" /api/v1/auth/login)" \
|
2026-03-01 04:08:53 +00:00
|
|
|
-H "Content-Type: application/json" \
|
2026-03-01 22:40:29 +00:00
|
|
|
-d "{\"email\":\"smoke-${TS}@dd0c.dev\",\"password\":\"smoketest123!\"}" 2>/dev/null || echo "")
|
|
|
|
|
local token
|
|
|
|
|
token=$(echo "$resp" | grep -o '"token":"[^"]*' | cut -d'"' -f4 || echo "")
|
2026-03-01 04:08:53 +00:00
|
|
|
if [ -n "$token" ]; then
|
2026-03-01 22:40:29 +00:00
|
|
|
check "Login: $name (:$port)" "pass"
|
2026-03-01 04:08:53 +00:00
|
|
|
else
|
2026-03-01 22:40:29 +00:00
|
|
|
check "Login: $name (:$port)" "fail"
|
2026-03-01 04:08:53 +00:00
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
hit() {
|
|
|
|
|
local port=$1 name=$2 endpoint=$3 token=$4
|
2026-03-01 04:08:53 +00:00
|
|
|
if [ -z "$token" ]; then
|
2026-03-01 22:40:29 +00:00
|
|
|
check "API: $name $endpoint (no token)" "fail"
|
2026-03-01 04:08:53 +00:00
|
|
|
return
|
|
|
|
|
fi
|
2026-03-01 22:40:29 +00:00
|
|
|
local code
|
|
|
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" \
|
2026-03-01 04:08:53 +00:00
|
|
|
-H "Authorization: Bearer $token" \
|
2026-03-01 22:40:29 +00:00
|
|
|
"$(url "$port" "$endpoint")" 2>/dev/null || echo "000")
|
|
|
|
|
if [ "$code" = "200" ] || [ "$code" = "201" ]; then
|
|
|
|
|
check "API: $name $endpoint" "pass"
|
2026-03-01 04:08:53 +00:00
|
|
|
else
|
2026-03-01 22:40:29 +00:00
|
|
|
check "API: $name $endpoint (HTTP $code)" "fail"
|
2026-03-01 04:08:53 +00:00
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
# ─── Run ───
|
2026-03-01 04:08:53 +00:00
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
echo -e "${YELLOW}dd0c Smoke Test — $(date -u '+%Y-%m-%d %H:%M UTC')${NC}"
|
|
|
|
|
echo -e "Target: ${PROTOCOL}://${BASE_URL}\n"
|
2026-03-01 04:08:53 +00:00
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
# 1. Infrastructure health
|
|
|
|
|
echo -e "${YELLOW}Infrastructure${NC}"
|
|
|
|
|
for pair in "drift:$PORT_DRIFT" "alert:$PORT_ALERT" "portal:$PORT_PORTAL" "run:$PORT_RUN" "cost:$PORT_COST"; do
|
|
|
|
|
name=${pair%%:*}; port=${pair##*:}
|
|
|
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" "$(url "$port" /health)" 2>/dev/null || echo "000")
|
|
|
|
|
[ "$code" = "200" ] && check "Health: $name (:$port)" "pass" || check "Health: $name (:$port) (HTTP $code)" "fail"
|
2026-03-01 04:08:53 +00:00
|
|
|
done
|
|
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
if [ "$SKIP_RUST" = false ]; then
|
|
|
|
|
for pair in "route-api:$PORT_ROUTE_API" "route-proxy:$PORT_ROUTE_PROXY"; do
|
|
|
|
|
name=${pair%%:*}; port=${pair##*:}
|
|
|
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" "$(url "$port" /health)" 2>/dev/null || echo "000")
|
|
|
|
|
[ "$code" = "200" ] && check "Health: $name (:$port)" "pass" || check "Health: $name (:$port) (HTTP $code)" "fail"
|
|
|
|
|
done
|
|
|
|
|
fi
|
2026-03-01 06:13:53 +00:00
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
# 2. Auth flow
|
|
|
|
|
echo -e "\n${YELLOW}Auth (signup → login → /me)${NC}"
|
|
|
|
|
|
|
|
|
|
ALERT_TOKEN=$(signup $PORT_ALERT "alert")
|
|
|
|
|
PORTAL_TOKEN=$(signup $PORT_PORTAL "portal")
|
|
|
|
|
COST_TOKEN=$(signup $PORT_COST "cost")
|
|
|
|
|
RUN_TOKEN=$(signup $PORT_RUN "run")
|
|
|
|
|
DRIFT_TOKEN=$(signup $PORT_DRIFT "drift")
|
2026-03-01 04:08:53 +00:00
|
|
|
|
2026-03-01 22:40:29 +00:00
|
|
|
login $PORT_ALERT "alert"
|
|
|
|
|
|
|
|
|
|
if [ -n "$ALERT_TOKEN" ]; then
|
|
|
|
|
me_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
|
|
|
-H "Authorization: Bearer $ALERT_TOKEN" \
|
|
|
|
|
"$(url $PORT_ALERT /api/v1/auth/me)" 2>/dev/null || echo "000")
|
|
|
|
|
[ "$me_code" = "200" ] && check "Auth /me: alert" "pass" || check "Auth /me: alert (HTTP $me_code)" "fail"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# 3. Product API endpoints
|
|
|
|
|
echo -e "\n${YELLOW}Product APIs${NC}"
|
|
|
|
|
|
|
|
|
|
hit $PORT_ALERT "alert" "/api/v1/incidents" "$ALERT_TOKEN"
|
|
|
|
|
hit $PORT_PORTAL "portal" "/api/v1/services" "$PORTAL_TOKEN"
|
|
|
|
|
hit $PORT_COST "cost" "/api/v1/baselines" "$COST_TOKEN"
|
|
|
|
|
hit $PORT_COST "cost" "/api/v1/anomalies" "$COST_TOKEN"
|
|
|
|
|
hit $PORT_RUN "run" "/api/v1/runbooks" "$RUN_TOKEN"
|
|
|
|
|
hit $PORT_DRIFT "drift" "/api/v1/stacks" "$DRIFT_TOKEN"
|
|
|
|
|
|
|
|
|
|
# 4. Webhook endpoints (should be accessible without auth)
|
|
|
|
|
echo -e "\n${YELLOW}Webhook endpoints (no auth)${NC}"
|
|
|
|
|
for pair in "$PORT_ALERT:alert"; do
|
|
|
|
|
port=${pair%%:*}; name=${pair##*:}
|
|
|
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
|
|
|
|
|
-H "Content-Type: application/json" -d '{"alertType":"metric_alert_monitor","body":"test"}' \
|
|
|
|
|
"$(url "$port" /webhooks/datadog/smoke-test)" 2>/dev/null || echo "000")
|
|
|
|
|
# 400/401/404 all mean the endpoint exists (404 = unknown tenant slug, which is expected)
|
|
|
|
|
if [ "$code" = "200" ] || [ "$code" = "400" ] || [ "$code" = "401" ] || [ "$code" = "404" ]; then
|
|
|
|
|
check "Webhook: $name /webhooks/datadog/:slug (HTTP $code)" "pass"
|
|
|
|
|
else
|
|
|
|
|
check "Webhook: $name /webhooks/datadog/:slug (HTTP $code)" "fail"
|
|
|
|
|
fi
|
|
|
|
|
done
|
2026-03-01 04:08:53 +00:00
|
|
|
|
|
|
|
|
# Summary
|
|
|
|
|
echo ""
|
2026-03-01 22:40:29 +00:00
|
|
|
echo -e "${YELLOW}═══════════════════════════${NC}"
|
|
|
|
|
if [ "$PASSED" -eq "$TOTAL" ]; then
|
|
|
|
|
echo -e "${GREEN}ALL PASSED: $PASSED/$TOTAL${NC}"
|
2026-03-01 04:08:53 +00:00
|
|
|
exit 0
|
|
|
|
|
else
|
2026-03-01 22:40:29 +00:00
|
|
|
echo -e "${RED}$PASSED/$TOTAL passed ($((TOTAL - PASSED)) failed)${NC}"
|
2026-03-01 04:08:53 +00:00
|
|
|
exit 1
|
|
|
|
|
fi
|