#!/usr/bin/env bash
# twohost-web-local.sh — run BOTH roles of crates/spt-daemon/tests/twohost_web.rs on ONE box
# (peer ip 127.0.0.1, fixed rig ports 7480/7481) as the gate's SEPARATE-INVOCATION witness:
# role A's three #[test] fns (fetch / range / deny) are distinct nextest cells, so a mutation
# arm can red ONE of them while the other two are WITNESSED green (not structurally skipped).
#
# usage: twohost-web-local.sh <gate-worktree> <label> [none|A|B]
#   A = owner deny branch → allow      (expect: deny cell RED, fetch + range GREEN)
#   B = owner strips the Range header  (expect: range cell RED, fetch + deny GREEN)
#   C = the WAN-ingress helper caller is a NO-OP (WEBSERVE W2 falsifier 1)
#       expect: helper cell RED on BOTH roles — A's helperline never gains a line and B is
#       never asked to serve — while fetch + range + deny stay GREEN. This is the arm that
#       proves the helper cells are driving the DAEMON and not each other: if they can pass
#       with the caller disabled, they were never witnessing the round trip.
# Writes <worktree>/.spt/twohost-web/<label>/{build,b,a}.{raw,exit}, mutated.line, revert.dirty.
# Identity trio + SPT_HOME are scrubbed for every child (a perch-launched battery inherits
# the endpoint identity and every daemon-stopping arm refuses — memory 2026-09-06).
set -u
GW=$1; LABEL=$2; MUT=${3:-none}
OUT=$GW/.spt/twohost-web/$LABEL
mkdir -p "$OUT"
cd "$GW" || exit 9
unset OWL_SESSION_ID SPT_AGENT_ID SPT_ENDPOINT_ID SPT_HOME
# ONE BOX: role A's cells bind port_a+0..2 and role B binds port_b, all with the same +20
# offset — adjacent 7460/7461 put A's cell 1 ON B's broker (bind fail, measured 02:49Z run
# "none"), so B sits at 7470 (udp 7490), ten clear of A's 7480..7482.
export SPT_TWO_HOST=1 SPT_TWO_HOST_SECRET=rig-doyle-w1 SPT_TWO_HOST_PEER_IP=127.0.0.1 \
       SPT_TWO_HOST_WAIT_SECS=${WAIT:-240} SPT_TWO_HOST_PORT_A=7460 SPT_TWO_HOST_PORT_B=7470
FILE=crates/spt-daemon/src/webproxy.rs
# Arm C mutates the CALLER, which lives on the ingress side, not the proxy.
FILE_C=crates/spt-daemon/src/wan.rs

apply_mutation() {
  case "$MUT" in
    none) return 0 ;;
    A) OLD='if access_check(&subject, origin_node, surface::WEB, InboundClass::Unsolicited).is_deny() {'
       NEW='if false && access_check(&subject, origin_node, surface::WEB, InboundClass::Unsolicited).is_deny() {' ;;
    B) OLD='let plan = webserve::plan_file(len, content_type, range.as_deref());'
       NEW='let plan = webserve::plan_file(len, content_type, None);' ;;
    C) FILE=$FILE_C
       OLD='    request_quoted_paths(msg, origin_node, &delivered_body, &perch_path);'
       NEW='    let _ = (&delivered_body, &perch_path); // FALSIFIER C: the ingress caller is a no-op' ;;
    *) echo "unknown mutation $MUT" >&2; exit 8 ;;
  esac
  printf '%s' "$OLD" > "$OUT/old.txt"; printf '%s' "$NEW" > "$OUT/new.txt"
  MATCH_COUNT=$(grep -cF -- "$OLD" "$FILE")
  echo "MATCH_COUNT=$MATCH_COUNT" | tee "$OUT/match.count"
  [ "$MATCH_COUNT" = "1" ] || { echo "REFUSE: need exactly one site" >&2; exit 7; }
  OLD="$OUT/old.txt" NEW="$OUT/new.txt" perl -0pi -e '
    BEGIN { local $/; open F, "<", $ENV{OLD}; $o = <F>; open G, "<", $ENV{NEW}; $n = <G>; }
    s/\Q$o\E/$n/' "$FILE"
  grep -nF -- "$NEW" "$FILE" | tee "$OUT/mutated.line"
  [ -s "$OUT/mutated.line" ] || { echo "REFUSE: mutation did not land" >&2; exit 6; }
}
revert_mutation() {
  [ "$MUT" = none ] && return 0
  git checkout -- "$FILE"
  git diff --stat -- "$FILE" | tee "$OUT/revert.dirty"
  echo "DIRTY_LINES=$(git diff -- "$FILE" | wc -l)" | tee -a "$OUT/revert.dirty"
}

apply_mutation || exit $?
date -u +%FT%TZ > "$OUT/started"
# Build once so the two role processes never race cargo's build lock.
cargo nextest run -p spt-daemon --test twohost_web --no-run > "$OUT/build.raw" 2>&1
echo $? > "$OUT/build.exit"
if [ "$(cat "$OUT/build.exit")" != "0" ]; then revert_mutation; exit 5; fi

SPT_TWO_HOST_ROLE=b cargo nextest run -p spt-daemon --test twohost_web --no-fail-fast --no-capture \
  > "$OUT/b.raw" 2>&1 &
BPID=$!
echo "$BPID" > "$OUT/b.pid"
sleep 10
SPT_TWO_HOST_ROLE=a cargo nextest run -p spt-daemon --test twohost_web --no-fail-fast --no-capture \
  > "$OUT/a.raw" 2>&1
echo $? > "$OUT/a.exit"
wait "$BPID"
echo $? > "$OUT/b.exit"
date -u +%FT%TZ > "$OUT/finished"
revert_mutation
echo "== $LABEL mut=$MUT  a.exit=$(cat "$OUT/a.exit")  b.exit=$(cat "$OUT/b.exit")  Summaries a=$(grep -c Summary "$OUT/a.raw") b=$(grep -c Summary "$OUT/b.raw")"
grep -E 'PASS|FAIL|panicked at' "$OUT/a.raw" | grep -E 'role_a|panicked' | sed 's/^/A: /'
grep -E 'PASS|FAIL|panicked at' "$OUT/b.raw" | grep -E 'role_b|panicked' | sed 's/^/B: /'
