magicciv/scripts/apricot-async-smoke.sh
Natalie 6216d97a76 feat(@projects/@magic-civilization): add async smoke test script
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-05 14:12:23 -04:00

81 lines
3.5 KiB
Bash
Executable file

#!/usr/bin/env bash
# apricot-async-smoke.sh — End-to-end smoke for the p2-64 launch/status/fetch protocol.
#
# Exercises a tiny batch (smoke 1 50) through the full async loop:
# 1. launch → bare stamp on stdout
# 2. status → valid JSON immediately, state in {running, unreachable}
# 3. wait loop → poll until state==complete (or fail)
# 4. fetch → rsync results to .local/iter/<stamp>/
# 5. verify → at least one game_*/turn_stats.jsonl present locally
#
# Skips gracefully (exit 0) if apricot is unreachable, so this can run on plum
# without blocking when the RUN host is offline.
#
# Usage:
# bash scripts/apricot-async-smoke.sh # default smoke 1 50
# POLL_TIMEOUT_S=600 bash scripts/apricot-async-smoke.sh # extend the wait
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
APRICOT="${APRICOT_SSH_ALIAS:-apricot}"
POLL_TIMEOUT_S="${POLL_TIMEOUT_S:-900}"
POLL_INTERVAL_S="${POLL_INTERVAL_S:-15}"
log() { printf '[%s] %s\n' "$(date +%H:%M:%S)" "$*" >&2; }
# ── Reachability gate: skip if apricot can't be reached at all. ──────────────
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "$APRICOT" 'echo ok' >/dev/null 2>&1; then
log "apricot unreachable; skipping smoke (exit 0)"
exit 0
fi
# ── Step 1: launch ───────────────────────────────────────────────────────────
log "launching smoke 1 50 …"
STAMP="$(bash "$SCRIPT_DIR/apricot-run.sh" launch smoke 1 50)"
if [[ -z "$STAMP" ]]; then
log "FAIL: launch returned empty stamp"
exit 1
fi
log "launched stamp=$STAMP"
# ── Step 2: status (must be valid JSON, must mention the stamp) ──────────────
STATUS_JSON="$(bash "$SCRIPT_DIR/apricot-run.sh" status "$STAMP")"
log "initial status: $STATUS_JSON"
case "$STATUS_JSON" in
*"\"stamp\":\"$STAMP\""*) ;;
*) log "FAIL: status JSON missing stamp field"; exit 1 ;;
esac
# ── Step 3: poll until complete | failed (with timeout) ──────────────────────
log "polling every ${POLL_INTERVAL_S}s up to ${POLL_TIMEOUT_S}s …"
DEADLINE=$(( $(date +%s) + POLL_TIMEOUT_S ))
STATE="running"
while (( $(date +%s) < DEADLINE )); do
STATUS_JSON="$(bash "$SCRIPT_DIR/apricot-run.sh" status "$STAMP" || true)"
STATE="$(echo "$STATUS_JSON" | sed -n 's/.*"state":"\([^"]*\)".*/\1/p')"
log "state=$STATE ($STATUS_JSON)"
case "$STATE" in
complete) break ;;
failed) log "FAIL: batch failed; journalctl --user -u mc-batch-$STAMP on $APRICOT"; exit 1 ;;
esac
sleep "$POLL_INTERVAL_S"
done
if [[ "$STATE" != "complete" ]]; then
log "FAIL: did not reach complete within ${POLL_TIMEOUT_S}s (last state=$STATE)"
exit 1
fi
# ── Step 4: fetch ────────────────────────────────────────────────────────────
LOCAL_DEST="$(bash "$SCRIPT_DIR/apricot-run.sh" fetch "$STAMP")"
log "fetched to: $LOCAL_DEST"
# ── Step 5: verify result presence ───────────────────────────────────────────
if ! find "$LOCAL_DEST" -path '*/game_*/turn_stats.jsonl' -type f | grep -q .; then
log "FAIL: no turn_stats.jsonl found under $LOCAL_DEST"
exit 1
fi
log "OK — async protocol smoke passed for stamp=$STAMP"