#!/usr/bin/env bash # Loop runner for the stress harness. # Runs api_stress.mjs each cycle with a fresh random seed, and runs ui_stress.mjs # every $UI_EVERY cycles (default 5). Logs JSON lines to runs/.jsonl. # Run this in tmux: tmux new -d -s quiz_stress 'bash run_loop.sh' set -uo pipefail cd "$(dirname "$0")" mkdir -p runs UI_EVERY=${UI_EVERY:-5} SLEEP_BETWEEN=${SLEEP_BETWEEN:-3} LOG="runs/run-$(date -u +%Y%m%dT%H%M%SZ).jsonl" SUM="runs/summary.jsonl" echo "{\"event\":\"loop_start\",\"ts\":\"$(date -u +%FT%TZ)\",\"log\":\"$LOG\",\"ui_every\":$UI_EVERY}" | tee -a "$SUM" cycle=0 total_pass=0 total_fail=0 total_warn=0 trap 'echo "{\"event\":\"loop_stop\",\"ts\":\"$(date -u +%FT%TZ)\",\"cycles\":'$cycle',\"total_pass\":'$total_pass',\"total_fail\":'$total_fail',\"total_warn\":'$total_warn'}" | tee -a "$SUM"; exit 0' INT TERM while true; do cycle=$((cycle + 1)) seed=$(( (RANDOM * 32768 + RANDOM) % 1000000 )) port=$((8200 + (cycle % 50))) printf '\n----- cycle %d (seed=%d port=%d) api -----\n' "$cycle" "$seed" "$port" | tee -a "$LOG" out=$(timeout 120 node api_stress.mjs "$seed" "$port" 2>&1) echo "$out" | tee -a "$LOG" >/dev/null summary=$(echo "$out" | grep '"runner"' | grep '"summary"' | tail -1) if [ -n "$summary" ]; then p=$(echo "$summary" | sed -n 's/.*"pass":\([0-9]*\).*/\1/p') f=$(echo "$summary" | sed -n 's/.*"fail":\([0-9]*\).*/\1/p') w=$(echo "$summary" | sed -n 's/.*"warn":\([0-9]*\).*/\1/p') total_pass=$((total_pass + ${p:-0})) total_fail=$((total_fail + ${f:-0})) total_warn=$((total_warn + ${w:-0})) echo "{\"event\":\"cycle\",\"ts\":\"$(date -u +%FT%TZ)\",\"cycle\":$cycle,\"kind\":\"api\",\"seed\":$seed,\"port\":$port,\"pass\":${p:-0},\"fail\":${f:-0},\"warn\":${w:-0},\"running_pass\":$total_pass,\"running_fail\":$total_fail,\"running_warn\":$total_warn}" | tee -a "$SUM" else echo "{\"event\":\"cycle\",\"ts\":\"$(date -u +%FT%TZ)\",\"cycle\":$cycle,\"kind\":\"api\",\"seed\":$seed,\"port\":$port,\"status\":\"NO_SUMMARY\"}" | tee -a "$SUM" fi if [ $((cycle % UI_EVERY)) -eq 0 ]; then printf '\n----- cycle %d (seed=%d port=%d) ui -----\n' "$cycle" "$seed" "$((port + 100))" | tee -a "$LOG" out=$(timeout 180 node ui_stress.mjs "$seed" "$((port + 100))" 2>&1) echo "$out" | tee -a "$LOG" >/dev/null summary=$(echo "$out" | grep '"runner"' | grep '"summary"' | tail -1) if [ -n "$summary" ]; then p=$(echo "$summary" | sed -n 's/.*"pass":\([0-9]*\).*/\1/p') f=$(echo "$summary" | sed -n 's/.*"fail":\([0-9]*\).*/\1/p') w=$(echo "$summary" | sed -n 's/.*"warn":\([0-9]*\).*/\1/p') total_pass=$((total_pass + ${p:-0})) total_fail=$((total_fail + ${f:-0})) total_warn=$((total_warn + ${w:-0})) echo "{\"event\":\"cycle\",\"ts\":\"$(date -u +%FT%TZ)\",\"cycle\":$cycle,\"kind\":\"ui\",\"seed\":$seed,\"port\":$((port + 100)),\"pass\":${p:-0},\"fail\":${f:-0},\"warn\":${w:-0},\"running_pass\":$total_pass,\"running_fail\":$total_fail,\"running_warn\":$total_warn}" | tee -a "$SUM" else echo "{\"event\":\"cycle\",\"ts\":\"$(date -u +%FT%TZ)\",\"cycle\":$cycle,\"kind\":\"ui\",\"seed\":$seed,\"port\":$((port + 100)),\"status\":\"NO_SUMMARY\"}" | tee -a "$SUM" fi fi sleep "$SLEEP_BETWEEN" done