← back to Dw Domain Fleet
scripts/test-assert-grids.sh
79 lines
#!/usr/bin/env bash
# test-assert-grids.sh — NEGATIVE TEST for the deploy grid gate.
#
# CLAUDE.md amendment 3: "A check ships with a negative test proving it goes RED
# on an injected fault, or it does not ship. A positive-only test on a detector
# proves nothing — it confirms the happy path and leaves the entire purpose of
# the component unverified."
#
# So this breaks assert-grids.js on purpose, four ways, and asserts it goes red
# each time — then proves it goes green on a healthy fixture, so we know red
# isn't just its resting state.
#
# 1. EMPTY POOL /health {serving:0} -> FAIL (the TK-11463 outage)
# 2. EMPTY GRID pool>0 but 0 cards rendered -> FAIL (render broke downstream)
# 3. DEAD ORIGIN nothing listening -> WARN (NOT_MEASURED, never PASS)
# 4. NO CONTRACT /health without `serving` -> WARN (NOT_MEASURED, never PASS)
# 5. HEALTHY pool>0 and cards>0 -> PASS
#
# Run: bash scripts/test-assert-grids.sh
set -uo pipefail
cd "$(dirname "$0")/.."
PASSED=0; FAILED=0
fixture_pid=""
cleanup() { [ -n "$fixture_pid" ] && kill "$fixture_pid" 2>/dev/null; }
trap cleanup EXIT
# $1=name $2=fixture mode $3=expected exit code $4=expected verdict substring
run_case() {
local name="$1" mode="$2" want_code="$3" want_verdict="$4"
local port out code
port=$(node -e 'const s=require("net").createServer();s.listen(0,()=>{console.log(s.address().port);s.close()})')
if [ "$mode" != "dead" ]; then
MODE="$mode" PORT="$port" node scripts/fixtures/grid-fixture.js & fixture_pid=$!
# wait for listen
for _ in $(seq 1 40); do
curl -fsS -m 1 "http://127.0.0.1:$port/health" >/dev/null 2>&1 && break
# the no-contract fixture still answers 200, the dead one never does
sleep 0.1
done
fi
out=$(node scripts/assert-grids.js --json --test --base "http://127.0.0.1:$port" --timeout 3000 2>&1); code=$?
[ -n "$fixture_pid" ] && { kill "$fixture_pid" 2>/dev/null; wait "$fixture_pid" 2>/dev/null; fixture_pid=""; }
local got_verdict; got_verdict=$(printf '%s' "$out" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).verdict)}catch{console.log("UNPARSEABLE")}})')
if [ "$code" = "$want_code" ] && [ "$got_verdict" = "$want_verdict" ]; then
echo " ok $name -> exit $code, verdict $got_verdict (expected $want_code/$want_verdict)"
PASSED=$((PASSED+1))
else
echo " FAIL $name -> exit $code, verdict $got_verdict (EXPECTED $want_code/$want_verdict)"
printf '%s\n' "$out" | head -20
FAILED=$((FAILED+1))
fi
}
echo "==> negative tests: the gate MUST go red on an injected fault"
run_case "1. empty pool (serving:0) " empty-pool 1 FAIL
run_case "2. empty grid (0 cards, pool>0)" empty-grid 1 FAIL
run_case "3. dead origin (nothing listens)" dead 2 WARN
run_case "4. /health with no serving field" no-contract 2 WARN
echo "==> positive test: and MUST go green when genuinely healthy"
run_case "5. healthy (pool>0, cards>0) " healthy 0 PASS
echo "==> $PASSED passed, $FAILED failed"
# Guard the guard: the seam must be INERT without --test, or a scheduled job
# could silently measure a fixture. Without --test, --base is ignored and the
# script probes the real domains — so it must NOT report the fixture's verdict.
echo "==> seam-inertness check (--base without --test must be ignored)"
if node scripts/assert-grids.js --json --base http://127.0.0.1:1 --timeout 3000 2>&1 | grep -q '"origin_override": null'; then
echo " ok --base is inert without --test"; PASSED=$((PASSED+1))
else
echo " FAIL --base leaked into a non-test run"; FAILED=$((FAILED+1))
fi
exit $((FAILED > 0 ? 1 : 0))