#!/bin/bash # Smoke Test Script for dd0c Docker Compose Stack # Tests health endpoints, tenant creation, and key API endpoints for all services set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Configuration BASE_URL="${BASE_URL:-localhost}" PROTOCOL="${PROTOCOL:-http}" 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 # Counters TOTAL=0 PASSED=0 show_help() { cat << EOF dd0c Smoke Test Usage: $0 [OPTIONS] Options: --help Show this help --base-url URL Set base URL (default: localhost) --protocol PROTO Set protocol (default: http) --skip-rust Skip Rust service checks (P1 route) Examples: $0 --base-url 192.168.86.11 BASE_URL=192.168.86.11 $0 --skip-rust EOF } SKIP_RUST=false while [[ $# -gt 0 ]]; do case $1 in --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 ;; esac done check() { local name=$1 status=$2 ((TOTAL++)) || true if [ "$status" = "pass" ]; then echo -e " ${GREEN}✓${NC} $name" >&2 ((PASSED++)) || true else echo -e " ${RED}✗${NC} $name" >&2 fi } 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" else check "Signup: $name (:$port) — $(echo "$resp" | head -c 80)" "fail" echo "" fi } login() { local port=$1 name=$2 local resp resp=$(curl -s -X POST "$(url "$port" /api/v1/auth/login)" \ -H "Content-Type: application/json" \ -d "{\"email\":\"smoke-${TS}@dd0c.dev\",\"password\":\"smoketest123!\"}" 2>/dev/null || echo "") local token token=$(echo "$resp" | grep -o '"token":"[^"]*' | cut -d'"' -f4 || echo "") if [ -n "$token" ]; then check "Login: $name (:$port)" "pass" else check "Login: $name (:$port)" "fail" fi } hit() { local port=$1 name=$2 endpoint=$3 token=$4 if [ -z "$token" ]; then check "API: $name $endpoint (no token)" "fail" return fi local code code=$(curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $token" \ "$(url "$port" "$endpoint")" 2>/dev/null || echo "000") if [ "$code" = "200" ] || [ "$code" = "201" ]; then check "API: $name $endpoint" "pass" else check "API: $name $endpoint (HTTP $code)" "fail" fi } # ─── Run ─── echo -e "${YELLOW}dd0c Smoke Test — $(date -u '+%Y-%m-%d %H:%M UTC')${NC}" echo -e "Target: ${PROTOCOL}://${BASE_URL}\n" # 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" done 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 # 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") 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 # Summary echo "" echo -e "${YELLOW}═══════════════════════════${NC}" if [ "$PASSED" -eq "$TOTAL" ]; then echo -e "${GREEN}ALL PASSED: $PASSED/$TOTAL${NC}" exit 0 else echo -e "${RED}$PASSED/$TOTAL passed ($((TOTAL - PASSED)) failed)${NC}" exit 1 fi