#!/usr/bin/env bash ## Performance test runner for the missions service. ## Scenarios: NFT-PERF-01 (cascade P50 <=50ms), NFT-PERF-03 (health P50 <=10ms), ## NFT-PERF-02 (cascade with full chain, regression baseline), NFT-PERF-04 ## (mission list P95, regression baseline). Spec lives in ## _docs/02_document/tests/performance-tests.md. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" COMPOSE_FILE="$PROJECT_ROOT/docker-compose.test.yml" RESULTS_DIR="$PROJECT_ROOT/test-results" TEST_PROJECT_DIR="$PROJECT_ROOT/tests/Azaion.Missions.E2E.Tests" KEEP_RUNNING=false for arg in "$@"; do case "$arg" in --keep-running) KEEP_RUNNING=true ;; -h|--help) cat <&2 exit 64 ;; esac done cleanup() { local exit_code=$? if [ "$KEEP_RUNNING" = "true" ]; then echo "[run-perf] --keep-running set; leaving compose stack up." >&2 else echo "[run-perf] tearing down compose stack..." >&2 docker compose -f "$COMPOSE_FILE" down -v --remove-orphans >/dev/null 2>&1 || true fi exit "$exit_code" } trap cleanup EXIT mkdir -p "$RESULTS_DIR" ## --- Install dependencies --- command -v docker >/dev/null 2>&1 || { echo "[run-perf] ERROR: docker is required but not installed on PATH." >&2 exit 2 } docker compose version >/dev/null 2>&1 || { echo "[run-perf] ERROR: docker compose v2 plugin is required." >&2 exit 2 } if [ ! -d "$TEST_PROJECT_DIR" ]; then cat >&2 <&2 docker compose -f "$COMPOSE_FILE" up -d --build postgres-test missions echo "[run-perf] waiting for missions /health (timeout 60s)..." >&2 ATTEMPTS=0 until [ "$ATTEMPTS" -ge 60 ]; do if curl -sf http://localhost:5002/health >/dev/null 2>&1; then break fi ATTEMPTS=$((ATTEMPTS + 1)) sleep 1 done if [ "$ATTEMPTS" -ge 60 ]; then echo "[run-perf] ERROR: missions did not become healthy within 60s." >&2 exit 3 fi ## --- Run perf scenarios --- ## The scenarios live in the same xUnit project as the blackbox suite, but are ## tagged [Trait("Category","Perf")] so they only run under this filter. Each ## scenario reports its computed P50/P95 to test-results/perf.csv. echo "[run-perf] running performance scenarios..." >&2 docker compose -f "$COMPOSE_FILE" --profile test build e2e-consumer docker compose -f "$COMPOSE_FILE" --profile test run --rm \ -e PERF_RESULTS_FILE=/app/results/perf.csv \ e2e-consumer dotnet test \ /app/Azaion.Missions.E2E.Tests.csproj \ --filter Category=Perf \ --logger "trx;LogFileName=perf.trx" TEST_EXIT=$? ## --- Compare against thresholds --- ## The xUnit Perf tests already enforce per-scenario thresholds (NFT-PERF-* `Pass ## criteria` in performance-tests.md). A failed assertion -> non-zero TEST_EXIT. ## This script just propagates the verdict; per-scenario detail is in perf.csv. if [ "$TEST_EXIT" -eq 0 ]; then echo "[run-perf] ALL THRESHOLDS MET." >&2 if [ -f "$RESULTS_DIR/perf.csv" ]; then echo "[run-perf] per-scenario detail: $RESULTS_DIR/perf.csv" >&2 fi else echo "[run-perf] THRESHOLD FAILURES (exit code $TEST_EXIT)." >&2 echo "[run-perf] missions logs (last 50 lines):" >&2 docker compose -f "$COMPOSE_FILE" logs --tail=50 missions >&2 || true fi exit "$TEST_EXIT"