ATlast — you'll never need to find your favorites on another platform again. Find your favs in the ATmosphere.
atproto
17
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 216 lines 7.8 kB view raw
1#!/bin/bash 2# ═══════════════════════════════════════════════════════ 3# ATlast Docker Stack Smoke Test 4# Validates the full stack is running and responding. 5# Usage: bash scripts/docker-smoke-test.sh 6# ═══════════════════════════════════════════════════════ 7 8set -euo pipefail 9 10# ── Configuration ────────────────────────────────────── 11COMPOSE_FILE="docker/docker-compose.yml" 12BASE_URL="http://localhost" 13MAX_WAIT_SECONDS=90 14POLL_INTERVAL=5 15 16# ── Color output ─────────────────────────────────────── 17RED='\033[0;31m' 18GREEN='\033[0;32m' 19YELLOW='\033[1;33m' 20NC='\033[0m' # No Color 21 22PASS=0 23FAIL=0 24 25# ── Helper functions ─────────────────────────────────── 26 27log_info() { echo -e "${YELLOW}[INFO]${NC} $1"; } 28log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; PASS=$((PASS + 1)); } 29log_fail() { echo -e "${RED}[FAIL]${NC} $1"; FAIL=$((FAIL + 1)); } 30 31check_status() { 32 local description="$1" 33 local url="$2" 34 local expected_status="$3" 35 36 local actual_status 37 actual_status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>/dev/null || echo "000") 38 39 if [ "$actual_status" = "$expected_status" ]; then 40 log_pass "$description → HTTP $actual_status (expected $expected_status)" 41 else 42 log_fail "$description → HTTP $actual_status (expected $expected_status) — URL: $url" 43 fi 44} 45 46check_json_field() { 47 local description="$1" 48 local url="$2" 49 local field="$3" 50 51 local response 52 response=$(curl -s --max-time 10 "$url" 2>/dev/null || echo "{}") 53 54 # Use node to parse JSON — available on any system running this stack. 55 local value 56 value=$(node -e " 57 try { 58 const data = JSON.parse(process.argv[1]); 59 const keys = process.argv[2].split('.'); 60 let val = data; 61 for (const key of keys) { val = val[key]; } 62 process.stdout.write(val !== undefined ? 'found' : 'missing'); 63 } catch (e) { 64 process.stdout.write('error'); 65 } 66 " "$response" "$field" 2>/dev/null || echo "error") 67 68 if [ "$value" = "found" ]; then 69 log_pass "$description → field '$field' present" 70 else 71 log_fail "$description → field '$field' missing or response unparseable" 72 fi 73} 74 75wait_for_healthy() { 76 local service_name="$1" 77 local url="$2" 78 local elapsed=0 79 80 log_info "Waiting for $service_name to become healthy (up to ${MAX_WAIT_SECONDS}s)..." 81 82 while [ $elapsed -lt $MAX_WAIT_SECONDS ]; do 83 local actual_status 84 actual_status=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$url" 2>/dev/null || echo "000") 85 86 # Accept 200 (health ok) or 401 (auth endpoint, service is up) as "alive" 87 if [ "$actual_status" = "200" ] || [ "$actual_status" = "401" ]; then 88 log_info "$service_name is responding (HTTP $actual_status) after ${elapsed}s" 89 return 0 90 fi 91 92 sleep $POLL_INTERVAL 93 elapsed=$((elapsed + POLL_INTERVAL)) 94 log_info " ... still waiting (${elapsed}s / ${MAX_WAIT_SECONDS}s, HTTP $actual_status)" 95 done 96 97 echo -e "${RED}[ERROR]${NC} $service_name did not become healthy within ${MAX_WAIT_SECONDS}s" 98 return 1 99} 100 101show_logs_for_service() { 102 local service="$1" 103 echo "" 104 echo -e "${YELLOW}═══ Logs for: $service ═══${NC}" 105 docker compose -f "$COMPOSE_FILE" logs --tail=30 "$service" 2>/dev/null || true 106 echo "" 107} 108 109# ── Main ──────────────────────────────────────────────── 110 111echo "" 112echo -e "${YELLOW}╔══════════════════════════════════════════╗${NC}" 113echo -e "${YELLOW}║ ATlast Docker Stack Smoke Test ║${NC}" 114echo -e "${YELLOW}╚══════════════════════════════════════════╝${NC}" 115echo "" 116 117# ── Step 1: Build and start the stack ────────────────── 118log_info "Building and starting the stack..." 119docker compose -f "$COMPOSE_FILE" up --build -d 120 121echo "" 122log_info "Stack started. Waiting for services to initialize..." 123echo "" 124 125# ── Step 2: Wait for API health endpoint ─────────────── 126if ! wait_for_healthy "API" "$BASE_URL/api/health"; then 127 echo "" 128 echo -e "${RED}API did not start. Showing logs for debugging:${NC}" 129 show_logs_for_service "api" 130 show_logs_for_service "database" 131 show_logs_for_service "redis" 132 exit 1 133fi 134 135echo "" 136echo -e "${YELLOW}─── Running endpoint checks ───${NC}" 137echo "" 138 139# ── Step 3: API health check ─────────────────────────── 140check_status \ 141 "GET /api/health → 200 OK" \ 142 "$BASE_URL/api/health" \ 143 "200" 144 145check_json_field \ 146 "GET /api/health → has 'status' field" \ 147 "$BASE_URL/api/health" \ 148 "status" 149 150# ── Step 4: Auth endpoints (unauthenticated) ─────────── 151 152# 401 proves: API is running AND auth middleware is working correctly. 153# A missing session returning 401 is the expected, correct behavior. 154check_status \ 155 "GET /api/auth/session → 401 (no session cookie, auth middleware active)" \ 156 "$BASE_URL/api/auth/session" \ 157 "401" 158 159check_status \ 160 "GET /api/auth/client-metadata.json → 200" \ 161 "$BASE_URL/api/auth/client-metadata.json" \ 162 "200" 163 164check_json_field \ 165 "GET /api/auth/client-metadata.json → has 'client_id' field" \ 166 "$BASE_URL/api/auth/client-metadata.json" \ 167 "client_id" 168 169check_status \ 170 "GET /api/auth/jwks → 200" \ 171 "$BASE_URL/api/auth/jwks" \ 172 "200" 173 174check_json_field \ 175 "GET /api/auth/jwks → has 'keys' field" \ 176 "$BASE_URL/api/auth/jwks" \ 177 "keys" 178 179# ── Step 5: Frontend is served ───────────────────────── 180check_status \ 181 "GET / → 200 (frontend HTML served by nginx)" \ 182 "$BASE_URL/" \ 183 "200" 184 185# ── Step 6: Docker service health summary ────────────── 186echo "" 187echo -e "${YELLOW}─── Docker service status ───${NC}" 188docker compose -f "$COMPOSE_FILE" ps 189echo "" 190 191# ── Step 7: Show results ─────────────────────────────── 192echo -e "${YELLOW}─── Results ────────────────────────────────────${NC}" 193echo -e " ${GREEN}Passed: $PASS${NC}" 194echo -e " ${RED}Failed: $FAIL${NC}" 195echo "" 196 197if [ $FAIL -gt 0 ]; then 198 echo -e "${RED}Smoke test FAILED ($FAIL checks failed).${NC}" 199 echo "" 200 echo "Showing logs for potentially failing services:" 201 202 for service in api worker database redis frontend; do 203 STATUS=$(docker compose -f "$COMPOSE_FILE" ps --format "{{.Service}} {{.Status}}" 2>/dev/null \ 204 | grep "^$service " | awk '{print $2}' || echo "unknown") 205 if [[ "$STATUS" != *"Up"* ]] || [[ "$STATUS" == *"unhealthy"* ]]; then 206 show_logs_for_service "$service" 207 fi 208 done 209 210 exit 1 211else 212 echo -e "${GREEN}All smoke tests passed!${NC}" 213 echo "" 214 echo "The stack is running. Access the app at: $BASE_URL" 215 exit 0 216fi