#!/usr/bin/env bash # test-matrix - run integration tests across all backend combinations # # combinations tested: # 1. sqlite + memory broker # 2. sqlite + redis broker # 3. postgres + memory broker # 4. postgres + redis broker # # usage: # ./scripts/test-matrix # run full matrix (default) # ./scripts/test-matrix --quick # sqlite + memory only (fast local dev) # ./scripts/test-matrix --no-cleanup # keep containers running after set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } step() { echo -e "${BLUE}[STEP]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*"; } matrix() { echo -e "${CYAN}[MATRIX]${NC} $*"; } SERVER_PID="" TEST_PORT=4202 POSTGRES_PORT=5434 REDIS_PORT=6380 CLEANUP=true QUICK_MODE=false # parse args for arg in "$@"; do case $arg in --quick) QUICK_MODE=true ;; --no-cleanup) CLEANUP=false ;; esac done cleanup() { if [[ -n "$SERVER_PID" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then kill "$SERVER_PID" 2>/dev/null || true wait "$SERVER_PID" 2>/dev/null || true fi lsof -ti:$TEST_PORT 2>/dev/null | xargs -r kill 2>/dev/null || true rm -f /tmp/prefect-matrix-*.db 2>/dev/null || true } trap cleanup EXIT wait_for_server() { local timeout=${1:-30} local count=0 while [[ $count -lt $timeout ]]; do if curl -s "http://localhost:$TEST_PORT/api/health" >/dev/null 2>&1; then return 0 fi sleep 0.5 ((count++)) done return 1 } wait_for_postgres() { local timeout=${1:-30} local count=0 while [[ $count -lt $timeout ]]; do if docker exec prefect-test-matrix-postgres pg_isready -U prefect >/dev/null 2>&1; then return 0 fi sleep 1 ((count++)) done return 1 } start_services() { step "Starting docker services (postgres + redis)..." # stop any existing test containers docker rm -f prefect-test-matrix-postgres prefect-test-matrix-redis 2>/dev/null || true # start postgres docker run -d \ --name prefect-test-matrix-postgres \ -e POSTGRES_USER=prefect \ -e POSTGRES_PASSWORD=prefect \ -e POSTGRES_DB=prefect_test \ -p "${POSTGRES_PORT}:5432" \ postgres:16-alpine >/dev/null 2>&1 # start redis docker run -d \ --name prefect-test-matrix-redis \ -p "${REDIS_PORT}:6379" \ redis:7-alpine >/dev/null 2>&1 # wait for postgres if ! wait_for_postgres 30; then error "PostgreSQL failed to start" return 1 fi # wait for redis local count=0 while [[ $count -lt 30 ]]; do if redis-cli -p "$REDIS_PORT" ping >/dev/null 2>&1; then break fi sleep 0.5 ((count++)) done info "Services ready" } stop_services() { if [[ "$CLEANUP" == "true" ]]; then step "Stopping docker services..." docker rm -f prefect-test-matrix-postgres prefect-test-matrix-redis 2>/dev/null || true else info "Leaving containers running (--no-cleanup)" fi } # run a single test configuration run_test() { local db_backend="$1" local broker_backend="$2" local label="${db_backend}+${broker_backend}" matrix "Testing: $label" # prepare environment local db_path="/tmp/prefect-matrix-${db_backend}-${broker_backend}-$$.db" rm -f "$db_path" export PREFECT_SERVER_PORT=$TEST_PORT export PREFECT_SERVER_LOGGING_LEVEL=WARNING export PREFECT_BROKER_BACKEND="$broker_backend" if [[ "$db_backend" == "sqlite" ]]; then export PREFECT_DATABASE_BACKEND=sqlite export PREFECT_DATABASE_PATH="$db_path" unset PREFECT_DATABASE_URL 2>/dev/null || true else export PREFECT_DATABASE_BACKEND=postgres export PREFECT_DATABASE_URL="postgresql://prefect:prefect@localhost:${POSTGRES_PORT}/prefect_test" # truncate all tables between tests (safer than DROP SCHEMA which breaks connections) docker exec prefect-test-matrix-postgres psql -U prefect -d prefect_test -c " DO \$\$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END \$\$; " >/dev/null 2>&1 || true fi if [[ "$broker_backend" == "redis" ]]; then export PREFECT_REDIS_MESSAGING_HOST=localhost export PREFECT_REDIS_MESSAGING_PORT=$REDIS_PORT # clear redis between tests redis-cli -p "$REDIS_PORT" FLUSHALL >/dev/null 2>&1 || true fi # start server "$PROJECT_DIR/zig-out/bin/prefect-server" & SERVER_PID=$! if ! wait_for_server 30; then error "[$label] Server failed to start" return 1 fi # run test suite step "[$label] Running API tests..." if PREFECT_API_URL="http://localhost:$TEST_PORT/api" "$PROJECT_DIR/scripts/test-api-sequence" >/dev/null 2>&1; then info "[$label] PASSED" local result=0 else error "[$label] FAILED" local result=1 fi # stop server kill "$SERVER_PID" 2>/dev/null || true wait "$SERVER_PID" 2>/dev/null || true SERVER_PID="" # ensure port is released before next test local port_wait=0 while [[ $port_wait -lt 20 ]] && lsof -ti:$TEST_PORT >/dev/null 2>&1; do lsof -ti:$TEST_PORT 2>/dev/null | xargs -r kill 2>/dev/null || true sleep 0.25 ((port_wait++)) done rm -f "$db_path" return $result } main() { echo "" matrix "============================================" matrix " prefect-server test matrix" matrix "============================================" echo "" # build first step "Building..." (cd "$PROJECT_DIR" && zig build) || { error "Build failed"; exit 1; } # run unit tests step "Running unit tests..." (cd "$PROJECT_DIR" && zig build test --summary all 2>&1) || { error "Unit tests failed"; exit 1; } echo "" if [[ "$QUICK_MODE" == "true" ]]; then matrix "Quick mode: sqlite + memory only" echo "" run_test sqlite memory || exit 1 echo "" matrix "============================================" matrix " QUICK TEST PASSED (1/4 combinations)" matrix "============================================" echo "" warn "Run without --quick to test full matrix" exit 0 fi # start services for full matrix start_services || exit 1 echo "" # track results local passed=0 local failed=0 declare -a results=() # test all 4 combinations for db in sqlite postgres; do for broker in memory redis; do step "Starting test: ${db}+${broker}" if run_test "$db" "$broker"; then results+=("${GREEN}PASS${NC} ${db}+${broker}") ((passed++)) || true else results+=("${RED}FAIL${NC} ${db}+${broker}") ((failed++)) || true fi step "Completed test: ${db}+${broker}" echo "" done done # cleanup stop_services # summary echo "" matrix "============================================" matrix " TEST MATRIX RESULTS" matrix "============================================" for r in "${results[@]}"; do echo -e " $r" done echo "" matrix "Passed: $passed / 4" if [[ $failed -gt 0 ]]; then error "Matrix test FAILED" exit 1 fi echo "" matrix "============================================" matrix " ALL 4 COMBINATIONS PASSED" matrix "============================================" echo "" } main