#!/usr/bin/env bash
# tests/tenant-offboard/run-tests.sh -- mock harness for scripts/tenant-offboard.sh
# Scenarios: blocklist=20, missing-domain=20, audit=0 (ZERO mutations), tty-mismatch=24,
#            apply-happy=0, cluster-DELETE_FAILED=21. Requires no cloud access.
set -u
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"; SCRIPT="$SD/../../scripts/tenant-offboard.sh"
W=$(mktemp -d); trap 'rm -rf "$W"' EXIT
mkdir -p "$W/bin" "$W/vault-init"; touch "$W/admin-openrc" "$W/vault-init/vault-ca-root.pem"
printf 'username=acme-svc\nproject_id=%s\npassword=pw\n' "$(python3 -c "print('a'*32)")" > "$W/acme-svc-cred.txt"
printf '#!/usr/bin/env bash\nexit 0\n' > "$W/bin/sleep"
cat > "$W/bin/openstack" <<'OSMOCK'
#!/usr/bin/env bash
S="${MOCK_SCEN:-happy}"; ST="${MOCK_STATE:-/home/claude/mock6/state}"; mkdir -p "$ST"
mut(){ echo "$*" >> "$ST/mutations"; }
DOM=$(python3 -c "print('d'*32)"); U1=$(python3 -c "print('1'*32)"); U2=$(python3 -c "print('2'*32)")
P1=$(python3 -c "print('a'*32)")
case "$*" in
  "domain show acme"*)
    [ -f "$ST/domgone" ] && { echo "No domain with a name or ID of 'acme' exists."; exit 1; }
    echo "$DOM";;
  "domain show "*) echo "No domain found"; exit 1;;
  "user list --domain"*) printf '%s acme-svc\n%s acme-domain-admin\n' "$U1" "$U2";;
  "project list --domain"*) printf '%s acme-prod\n' "$P1";;
  "coe cluster list"*)
    if [ -f "$ST/cdel" ]; then
      N=$(cat "$ST/cn" 2>/dev/null || echo 0); N=$((N+1)); echo "$N" > "$ST/cn"
      if [ "$S" = delfail ]; then echo "cu1 acme-cluster DELETE_FAILED"
      elif [ "$N" -le 2 ]; then echo "cu1 acme-cluster DELETE_IN_PROGRESS"; fi
    else echo "cu1 acme-cluster CREATE_FAILED"; fi;;
  "coe cluster delete"*) mut "$*"; touch "$ST/cdel";;
  "trust list"*)
    if [ -f "$ST/tdel" ]; then echo "[]"; else printf '[{"ID":"tr1","Trustor User ID":"%s","Trustee User ID":"tee"}]\n' "$U1"; fi;;
  "trust show tr1"*) [ -f "$ST/tdel" ] && exit 1; echo "tr1";;
  "trust delete"*) mut "$*"; touch "$ST/tdel";;
  "loadbalancer list"*) [ -f "$ST/lbdel" ] || echo "lb1 old-lb ERROR";;
  "loadbalancer delete"*) mut "$*"; touch "$ST/lbdel";;
  "floating ip list"*) [ -f "$ST/fipdel" ] || echo "f1 10.12.5.99";;
  "floating ip delete"*) mut "$*"; touch "$ST/fipdel";;
  "server list"*) : ;;
  "router list"*) [ -f "$ST/rdel" ] || echo "r1 acme-router";;
  "router remove subnet"*|"router unset"*) mut "$*";;
  "router delete"*) mut "$*"; touch "$ST/rdel";;
  "subnet list"*) [ -f "$ST/sndel" ] || echo "sn1 acme-subnet";;
  "subnet delete"*) mut "$*"; touch "$ST/sndel";;
  "network list"*) [ -f "$ST/nwdel" ] || echo "nw1 acme-net";;
  "network delete"*)
    if [ "${MOCK_SCEN:-}" = sweepfail ]; then echo "Conflict: one or more ports still in use (HTTP 409)"; exit 1; fi
    mut "$*"; touch "$ST/nwdel";;
  "user delete"*|"project delete"*) mut "$*";;
  "domain set --disable"*) mut "$*";;
  "domain delete"*) mut "$*"; touch "$ST/domgone";;
esac
OSMOCK
cat > "$W/ptyrun.py" <<'PTY'
import pty, os, sys
cmd = sys.argv[1:-1]; feed = sys.argv[-1]
pid, fd = pty.fork()
if pid == 0:
    os.execvp(cmd[0], cmd)
os.write(fd, (feed + "\n").encode())
out = b""
while True:
    try:
        d = os.read(fd, 4096)
    except OSError:
        break
    if not d:
        break
    out += d
_, st = os.waitpid(pid, 0)
sys.stdout.write(out.decode(errors="replace"))
sys.exit(os.waitstatus_to_exitcode(st))
PTY
chmod +x "$W/bin/"*
P=0; F=0
chk(){ if [ "$2" = "$3" ]; then echo "PASS: $1 (exit $2)"; P=$((P+1)); else echo "FAIL: $1 (exit $2, want $3)"; F=$((F+1)); fi; }
export MOCK_STATE="$W/state"
HOME="$W" PATH="$W/bin:$PATH" bash "$SCRIPT" magnum --apply >/dev/null 2>&1; chk blocklist $? 20
rm -rf "$W/state"; HOME="$W" PATH="$W/bin:$PATH" bash "$SCRIPT" ghost >/dev/null 2>&1; chk missing-domain $? 20
rm -rf "$W/state"; HOME="$W" PATH="$W/bin:$PATH" bash "$SCRIPT" acme --audit >/dev/null 2>&1; RC=$?
MUT=$(cat "$W/state/mutations" 2>/dev/null | wc -l)
if [ "$RC" = 0 ] && [ "$MUT" = 0 ]; then echo "PASS: audit (exit 0, 0 mutations)"; P=$((P+1)); else echo "FAIL: audit (exit $RC, mutations $MUT)"; F=$((F+1)); fi
rm -rf "$W/state"; HOME="$W" PATH="$W/bin:$PATH" python3 "$W/ptyrun.py" bash "$SCRIPT" acme --apply WRONG >/dev/null 2>&1; chk tty-mismatch $? 24
rm -rf "$W/state"; HOME="$W" PATH="$W/bin:$PATH" python3 "$W/ptyrun.py" bash "$SCRIPT" acme --apply acme >/dev/null 2>&1; chk apply-happy $? 0
rm -rf "$W/state"; HOME="$W" MOCK_SCEN=delfail PATH="$W/bin:$PATH" python3 "$W/ptyrun.py" bash "$SCRIPT" acme --apply acme >/dev/null 2>&1; chk delete-failed $? 21
# DOCFIX-082 failure injection: a mid-sweep resource-delete failure must surface as exit 22,
# never as silent success (the pre-082 script lost mutation exit codes in display pipelines).
rm -rf "$W/state"; HOME="$W" MOCK_SCEN=sweepfail PATH="$W/bin:$PATH" python3 "$W/ptyrun.py" bash "$SCRIPT" acme --apply acme > "$W/sweepfail.out" 2>&1; RC=$?
if [ "$RC" = 22 ] && grep -q '\^ FAILED: openstack network delete' "$W/sweepfail.out"; then
  echo "PASS: sweep-failure-injection (exit 22, failure line printed)"; P=$((P+1))
else echo "FAIL: sweep-failure-injection (exit $RC, want 22 + FAILED line)"; F=$((F+1)); fi
echo; [ "$F" = 0 ] && { echo "ALL PASS ($P/7)"; exit 0; } || { echo "FAILURES: $F"; exit 1; }
