← 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))