68d236961a
- deploy/.env.example: placeholders only (no real URLs, proper comments) - frontend/Dockerfile: Node 20 stub server for compose build - deploy/tests/test-compose-health.sh: OPS-1 compose health smoke test - deploy/tests/test-woo-base-url.sh: GC-4/5/FUND-25 validation suite - backend/app/api/settings.py: PUT rejects woo_base_url (422), consumer keys (422), text() wrapping for SQLAlchemy 2.x, key primary key fix - backend/tests/contract/test_woo_base_url.py: 8 contract tests for GC-4/GC-5 Tests: 39/39 backend tests pass (8 new, 31 existing green) Deploy: 5/5 shell tests pass (Docker unavailable for compose-up path)
85 lines
3.3 KiB
Bash
85 lines
3.3 KiB
Bash
#!/usr/bin/env bash
|
|
# OPS-1: Compose health smoke test.
|
|
# Usage: run from repo root: bash deploy/tests/test-compose-health.sh
|
|
# Expected: compose up, wait for services, curl health endpoint, assert 200 + ok.
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
COMPOSE_FILE="$REPO_ROOT/deploy/docker-compose.yml"
|
|
ENV_FILE="$REPO_ROOT/deploy/.env"
|
|
APP_HOSTNAME="${APP_HOSTNAME:-}"
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
pass() { echo -e "${GREEN}PASS${NC} $*"; }
|
|
fail() { echo -e "${RED}FAIL${NC} $*"; exit 1; }
|
|
warn() { echo -e "${YELLOW}WARN${NC} $*"; }
|
|
|
|
# ── Prerequisites ──────────────────────────────────────────────────────────────
|
|
if ! command -v docker &>/dev/null; then
|
|
warn "docker not found — skipping compose up, continuing with validation-only tests"
|
|
SKIP_COMPOSE=1
|
|
else
|
|
SKIP_COMPOSE=0
|
|
fi
|
|
|
|
if ! command -v curl &>/dev/null; then
|
|
fail "curl is required for health check"
|
|
fi
|
|
|
|
# ── Compose up ─────────────────────────────────────────────────────────────────
|
|
if [ "$SKIP_COMPOSE" -eq 0 ]; then
|
|
pass "Docker available, proceeding with compose up"
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
fail ".env file not found at $ENV_FILE. Copy .env.example and fill values."
|
|
fi
|
|
pass "Starting compose stack..."
|
|
docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d --build 2>&1 || fail "docker compose up failed"
|
|
|
|
# Wait for services to be healthy
|
|
pass "Waiting for services to be ready (up to 120s)..."
|
|
MAX_WAIT=120
|
|
WAITED=0
|
|
while [ $WAITED -lt $MAX_WAIT ]; do
|
|
STATUS=$(docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" ps --format json 2>/dev/null | grep -c '"status":"running"' || true)
|
|
if [ "$STATUS" -eq 2 ]; then
|
|
pass "Both frontend and fastapi running"
|
|
break
|
|
fi
|
|
sleep 5
|
|
WAITED=$((WAITED + 5))
|
|
done
|
|
if [ "$STATUS" -ne 2 ]; then
|
|
warn "Services did not reach running state within ${MAX_WAIT}s — continuing with direct health check"
|
|
fi
|
|
fi
|
|
|
|
# ── Health check ───────────────────────────────────────────────────────────────
|
|
HEALTH_URL="https://${APP_HOSTNAME}/api/health"
|
|
if [ -z "$APP_HOSTNAME" ]; then
|
|
HEALTH_URL="http://127.0.0.1:18081/api/health"
|
|
warn "APP_HOSTNAME not set — using direct fastapi port for health check"
|
|
fi
|
|
|
|
pass "Checking health endpoint: $HEALTH_URL"
|
|
HTTP_CODE=$(curl -sk -o /dev/null -w '%{http_code}' --max-time 15 "$HEALTH_URL" 2>/dev/null || echo "000")
|
|
|
|
if [ "$HTTP_CODE" = "200" ]; then
|
|
BODY=$(curl -sk --max-time 15 "$HEALTH_URL" 2>/dev/null)
|
|
STATUS=$(echo "$BODY" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status',''))" 2>/dev/null || echo "")
|
|
if [ "$STATUS" = "ok" ]; then
|
|
pass "OPS-1: Health check passed (HTTP 200, status=ok)"
|
|
else
|
|
fail "OPS-1: Health check returned unexpected status: $STATUS"
|
|
fi
|
|
else
|
|
fail "OPS-1: Health check failed with HTTP $HTTP_CODE"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=== Compose health test complete ==="
|
|
exit 0
|