#!/usr/bin/env bash
# Tests for resume-tunnel.sh supervised-service spawn path (Task 602 + Task 757).
# Task 602 decoupled the connector into its own transient unit under
# cloudflared.slice; Task 757 changed that unit from a --scope (no restart
# policy) to a --service with Restart=always and --no-autoupdate.
# Stubs systemd-run, systemctl, and cloudflared binaries via a fake $PATH entry.
set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
RESUME="$SCRIPT_DIR/../resume-tunnel.sh"

PASS=0
FAIL=0

run_case() {
  local name="$1"; shift
  echo ""
  echo "=== CASE: $name ==="
  if "$@"; then
    PASS=$((PASS + 1))
    echo "PASS: $name"
  else
    FAIL=$((FAIL + 1))
    echo "FAIL: $name"
  fi
}

# Build a minimal install tree with stubs and state file.
# Args: root config_dir scope_active connect_success
setup() {
  local root="$1"
  local config_dir="${2:-.maxy-code}"
  local scope_active="${3:-inactive}"
  local connect_success="${4:-yes}"

  local home_dir="$root/home"
  local platform_root="$root/platform_root"
  local bin_dir="$root/bin"
  mkdir -p "$home_dir/$config_dir/cloudflared" \
           "$home_dir/$config_dir/logs" \
           "$platform_root/config" \
           "$bin_dir"

  cat > "$platform_root/config/brand.json" <<JSON
{"configDir": "$config_dir"}
JSON

  cat > "$home_dir/$config_dir/cloudflared/tunnel.state" <<JSON
{
  "tunnelId": "test-tunnel-id",
  "domain": "test.example.com",
  "configPath": "$home_dir/$config_dir/cloudflared/config.yml",
  "pid": 99999
}
JSON

  touch "$home_dir/$config_dir/cloudflared/cert.pem"
  touch "$home_dir/$config_dir/cloudflared/config.yml"

  local log_file="$home_dir/$config_dir/logs/cloudflared.log"

  # Stub cloudflared: does nothing.
  cat > "$bin_dir/cloudflared" <<'SH'
#!/bin/bash
sleep 0.1
SH
  chmod +x "$bin_dir/cloudflared"

  # Stub systemctl: is-active returns based on scope_active.
  cat > "$bin_dir/systemctl" <<SH
#!/bin/bash
if [[ "\$*" == *"is-active"* ]]; then
  if [[ "${scope_active}" == "active" ]]; then
    echo "active"; exit 0
  else
    echo "inactive"; exit 3
  fi
fi
if [[ "\$*" == *"--property=MainPID"* ]]; then
  echo "12345"
fi
exit 0
SH
  chmod +x "$bin_dir/systemctl"

  # Stub systemd-run: echoes every arg (so tests can assert the unit name, slice,
  # restart properties, and the connector argv), optionally writes connection line.
  cat > "$bin_dir/systemd-run" <<SH
#!/bin/bash
for arg in "\$@"; do
  echo "[stub] arg=\$arg"
  case "\$arg" in
    --unit=*) echo "[stub] unit=\${arg#--unit=}" ;;
    --slice=*) echo "[stub] slice=\${arg#--slice=}" ;;
  esac
done
if [[ "${connect_success}" == "yes" ]]; then
  echo "2026-06-02T00:00:00Z INF Registered tunnel connection connIndex=0" >> "${log_file}"
fi
exit 0
SH
  chmod +x "$bin_dir/systemd-run"
}

cleanup() { rm -rf "$1"; }

# CASE 1: skip branch — scope is active
case_skip_when_scope_active() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "active" "yes"

  local log="$root/home/.maxy-code/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0, got $rc"; return 1
  fi
  if ! echo "$log_content" | grep -q "already running.*service.*cloudflared-maxy-code.service"; then
    echo "  skip branch missing expected log line"
    echo "$log_content"
    return 1
  fi
  return 0
}

# CASE 2: spawn branch — scope inactive, connection succeeds
case_spawn_succeeds() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"

  local log="$root/home/.maxy-code/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0, got $rc"; return 1
  fi
  if ! echo "$log_content" | grep -q "spawned service=cloudflared-maxy-code.service"; then
    echo "  missing spawned service log line"; echo "$log_content"; return 1
  fi
  if ! echo "$log_content" | grep -q "tunnel verified"; then
    echo "  missing tunnel verified log line"; echo "$log_content"; return 1
  fi
  return 0
}

# CASE 3: spawn branch — no edge connection within timeout
case_spawn_no_connection() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "no"

  local log="$root/home/.maxy-code/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
  RESUME_TUNNEL_VERIFY_DEADLINE=2 \
    bash "$RESUME"
  local rc=$?

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0 even on failure, got $rc"; return 1
  fi
  if ! echo "$log_content" | grep -q "ERROR.*no edge connection"; then
    echo "  missing ERROR log line on connection failure"; echo "$log_content"; return 1
  fi
  return 0
}

# CASE 4: service unit name derived correctly from configDir (.real-agent → cloudflared-real-agent.service)
case_scope_unit_name_from_config_dir() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".real-agent" "inactive" "yes"

  local log="$root/home/.real-agent/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0, got $rc"; return 1
  fi
  if ! echo "$log_content" | grep -q "spawned service=cloudflared-real-agent.service"; then
    echo "  service unit name not derived correctly from .real-agent"
    echo "$log_content"; return 1
  fi
  return 0
}

# CASE 5: --slice=cloudflared flag passed to systemd-run
case_slice_flag_present() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"

  local log="$root/home/.maxy-code/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  # The systemd-run stub echoes "[stub] slice=cloudflared" to stdout,
  # which is redirected to LOG_FILE by the script's >> redirect.
  if ! echo "$log_content" | grep -q "\[stub\] slice=cloudflared"; then
    echo "  --slice=cloudflared not seen by systemd-run stub"
    echo "$log_content"; return 1
  fi
  return 0
}

# CASE 6: supervision contract — Restart=always, crash-loop limits, --no-autoupdate,
# and the unit is a .service (Task 757), all reaching systemd-run.
case_supervision_contract() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"

  local log="$root/home/.maxy-code/logs/cloudflared.log"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"

  local log_content
  log_content=$(cat "$log" 2>/dev/null || echo "")
  cleanup "$root"

  local missing=""
  echo "$log_content" | grep -q "\[stub\] arg=--unit=cloudflared-maxy-code.service" || missing="$missing unit=.service"
  echo "$log_content" | grep -q "\[stub\] arg=Restart=always" || missing="$missing Restart=always"
  echo "$log_content" | grep -q "\[stub\] arg=StartLimitIntervalSec=300" || missing="$missing StartLimitIntervalSec"
  echo "$log_content" | grep -q "\[stub\] arg=StartLimitBurst=5" || missing="$missing StartLimitBurst"
  echo "$log_content" | grep -q "\[stub\] arg=--no-autoupdate" || missing="$missing --no-autoupdate"
  if [[ -n "$missing" ]]; then
    echo "  systemd-run argv missing:$missing"
    echo "$log_content"; return 1
  fi
  return 0
}

# ---------------------------------------------------------------------------
# Task 833 — stray-connector reap.
#
# install_reap_stubs adds a `ps` stub and a `kill` recorder on top of setup().
# The ps stub reads a table file ($root/ps_table) of "<pid> <argv...>" lines and
# answers both query shapes the script uses: `-eo pid=,args=` (full table) and
# `-p <pid> -o args=` (single argv). The recorder ($root/bin/killrec, wired via
# RESUME_TUNNEL_KILL_CMD) appends -TERM/-KILL calls to $root/reap_log; on a `-0`
# liveness query it reports "gone" (exit 1) unless $root/reap_alive exists.
install_reap_stubs() {
  local root="$1"
  local bin_dir="$root/bin"

  cat > "$bin_dir/ps" <<SH
#!/bin/bash
table="$root/ps_table"
if [[ "\$*" == *"-p"* ]]; then
  pid=""; prev=""
  for a in "\$@"; do
    [[ "\$prev" == "-p" ]] && pid="\$a"
    prev="\$a"
  done
  awk -v want="\$pid" '{ if (\$1==want){ \$1=""; sub(/^ /,""); print } }' "\$table" 2>/dev/null
  exit 0
fi
cat "\$table" 2>/dev/null
exit 0
SH
  chmod +x "$bin_dir/ps"

  cat > "$bin_dir/killrec" <<SH
#!/bin/bash
if [[ "\$1" == "-0" ]]; then
  [[ -f "$root/reap_alive" ]] && exit 0 || exit 1
fi
echo "\$*" >> "$root/reap_log"
exit 0
SH
  chmod +x "$bin_dir/killrec"
}

# Write a config.yml carrying the brand tunnel id so the audit line can resolve it.
write_branded_config() {
  local cfg="$1"
  cat > "$cfg" <<'YML'
tunnel: test-tunnel-id
credentials-file: /dev/null
ingress:
  - service: http_status:404
YML
}

# Run resume-tunnel with the reap stubs + recorder active.
# Args: root config_dir [extra env assignments...]
run_with_reap() {
  local root="$1" config_dir="$2"; shift 2
  # Use env so any extra "NAME=VALUE" args (e.g. RESUME_TUNNEL_REAP_GRACE=0)
  # are applied as assignments — a variable-expanded NAME=VALUE word is NOT
  # treated as an assignment by bash's own simple-command parsing.
  env \
    HOME="$root/home" \
    PATH="$root/bin:$PATH" \
    MAXY_PLATFORM_ROOT="$root/platform_root" \
    RESUME_TUNNEL_KILL_CMD="$root/bin/killrec" \
    RESUME_TUNNEL_VERIFY_DEADLINE=2 \
    "$@" \
    bash "$RESUME"
}

# CASE A: stray present, service inactive → reap kills stray, leaves none, then spawns.
case_reap_stray_then_spawn() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  cat > "$root/ps_table" <<TBL
210086 cloudflared --origincert $cert --config $cfg tunnel run
777 cloudflared --origincert /home/other/.realagent/cloudflared/cert.pem --config /home/other/.realagent/cloudflared/config.yml tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$log" | grep -q "op=reap brand=maxy-code found=1 killed=1 survivor=none" || { echo "  census wrong"; echo "$log"; return 1; }
  echo "$rec" | grep -q -- "-TERM 210086" || { echo "  stray not SIGTERMed"; echo "$rec"; return 1; }
  echo "$rec" | grep -q "777" && { echo "  co-resident 777 was signalled"; echo "$rec"; return 1; }
  echo "$log" | grep -q "spawned service=cloudflared-maxy-code.service" || { echo "  did not spawn after reap"; echo "$log"; return 1; }
  return 0
}

# CASE B: supervised survivor + stray, service active → reap kills only stray, no spawn.
case_reap_keeps_survivor() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "active" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  # 12345 is the MainPID the systemctl stub reports.
  cat > "$root/ps_table" <<TBL
12345 cloudflared --no-autoupdate --origincert $cert --config $cfg tunnel run
210086 cloudflared --origincert $cert --config $cfg tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$log" | grep -q "op=reap brand=maxy-code found=2 killed=1 survivor=12345" || { echo "  census wrong"; echo "$log"; return 1; }
  echo "$rec" | grep -q -- "-TERM 210086" || { echo "  stray not SIGTERMed"; echo "$rec"; return 1; }
  echo "$rec" | grep -q "12345" && { echo "  survivor 12345 was signalled"; echo "$rec"; return 1; }
  echo "$log" | grep -q "spawned service=" && { echo "  spawned while already active"; echo "$log"; return 1; }
  echo "$log" | grep -q "already running" || { echo "  missing already-running log"; echo "$log"; return 1; }
  return 0
}

# CASE C: no stray, service active → idempotent, no kill, no spawn.
case_reap_idempotent() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "active" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  cat > "$root/ps_table" <<TBL
12345 cloudflared --no-autoupdate --origincert $cert --config $cfg tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$log" | grep -q "op=reap brand=maxy-code found=1 killed=0 survivor=12345" || { echo "  census wrong"; echo "$log"; return 1; }
  [ -z "$rec" ] || { echo "  recorder not empty"; echo "$rec"; return 1; }
  echo "$log" | grep -q "spawned service=" && { echo "  spawned while single+active"; echo "$log"; return 1; }
  return 0
}

# CASE D: audit line resolves the brand tunnel id from the victim's --config.
case_reap_audit_tunnel_id() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  cat > "$root/ps_table" <<TBL
210086 cloudflared --origincert $cert --config $cfg tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$log" | grep -q "op=reap brand=maxy-code target=210086 tunnel=test-tunnel-id" || { echo "  audit line missing brand tunnel id"; echo "$log"; return 1; }
  return 0
}

# CASE E: only a foreign (co-resident) connector present → untouched, brand still spawns.
case_reap_foreign_untouched() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  write_branded_config "$home/.maxy-code/cloudflared/config.yml"
  cat > "$root/ps_table" <<TBL
777 cloudflared --origincert /home/other/.realagent/cloudflared/cert.pem --config /home/other/.realagent/cloudflared/config.yml tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$log" | grep -q "op=reap brand=maxy-code found=0 killed=0 survivor=none" || { echo "  census wrong"; echo "$log"; return 1; }
  [ -z "$rec" ] || { echo "  foreign connector signalled"; echo "$rec"; return 1; }
  echo "$log" | grep -q "spawned service=cloudflared-maxy-code.service" || { echo "  did not spawn own connector"; echo "$log"; return 1; }
  return 0
}

# CASE G: service active but MainPID unresolved → must NOT reap (can't tell the
# live supervised connector from a stray), must skip without spawning.
case_reap_skip_when_mainpid_unresolved() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "active" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  # Override systemctl so MainPID resolves empty while is-active stays active
  # (the RestartSec transition window).
  cat > "$root/bin/systemctl" <<'SH'
#!/bin/bash
if [[ "$*" == *"is-active"* ]]; then echo "active"; exit 0; fi
if [[ "$*" == *"--property=MainPID"* ]]; then echo ""; fi
exit 0
SH
  chmod +x "$root/bin/systemctl"
  cat > "$root/ps_table" <<TBL
210086 cloudflared --no-autoupdate --origincert $cert --config $cfg tunnel run
TBL

  run_with_reap "$root" ".maxy-code"
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  [ -z "$rec" ] || { echo "  reaped while survivor unknown — would kill the live connector"; echo "$rec"; return 1; }
  echo "$log" | grep -q "skipped reason=mainpid-unresolved" || { echo "  missing mainpid-unresolved skip log"; echo "$log"; return 1; }
  echo "$log" | grep -q "spawned service=" && { echo "  spawned while active"; echo "$log"; return 1; }
  return 0
}

# CASE F: stray survives SIGTERM → escalates to SIGKILL.
case_reap_sigkill_escalation() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  install_reap_stubs "$root"
  local home="$root/home"
  local cert="$home/.maxy-code/cloudflared/cert.pem"
  local cfg="$home/.maxy-code/cloudflared/config.yml"
  write_branded_config "$cfg"
  touch "$root/reap_alive"   # recorder reports the proc still alive on -0
  cat > "$root/ps_table" <<TBL
210086 cloudflared --origincert $cert --config $cfg tunnel run
TBL

  run_with_reap "$root" ".maxy-code" RESUME_TUNNEL_REAP_GRACE=0
  local rec; rec=$(cat "$root/reap_log" 2>/dev/null || echo "")
  cleanup "$root"

  echo "$rec" | grep -q -- "-KILL 210086" || { echo "  no SIGKILL escalation"; echo "$rec"; return 1; }
  return 0
}

# ---------------------------------------------------------------------------
# Credentials-file (API-token) tunnel resume.
#
# A tunnel created via the API-token path has a credentials-file in config.yml
# and NO cert.pem. `cloudflared tunnel run` authenticates with the
# credentials-file, not the account cert, so resume-tunnel must spawn such a
# tunnel WITHOUT --origincert and must NOT refuse it for the missing cert.

# CASE H: token tunnel (credentials-file present, no cert) → spawns without --origincert.
case_token_tunnel_spawns_without_origincert() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  local home="$root/home"
  # Token model: remove the OAuth cert; point config.yml at an existing creds json.
  rm -f "$home/.maxy-code/cloudflared/cert.pem"
  local creds="$home/.maxy-code/cloudflared/test-tunnel-id.json"
  echo '{"AccountTag":"x","TunnelSecret":"y","TunnelID":"test-tunnel-id"}' > "$creds"
  cat > "$home/.maxy-code/cloudflared/config.yml" <<YML
tunnel: test-tunnel-id
credentials-file: $creds
ingress:
  - service: http_status:404
YML

  HOME="$home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then echo "  expected exit 0, got $rc"; return 1; fi
  echo "$log" | grep -q "spawned service=cloudflared-maxy-code.service" \
    || { echo "  token tunnel did not spawn"; echo "$log"; return 1; }
  echo "$log" | grep -q "op=resume-refused" \
    && { echo "  token tunnel wrongly refused"; echo "$log"; return 1; }
  echo "$log" | grep -q "\[stub\] arg=--origincert" \
    && { echo "  --origincert passed for a token tunnel (cert absent)"; echo "$log"; return 1; }
  return 0
}

# CASE I: configured tunnel, no usable run credential → loud resume-refused, no spawn.
# config.yml references a missing credentials-file AND there is no cert.pem.
case_refuse_when_no_run_credential() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  local home="$root/home"
  rm -f "$home/.maxy-code/cloudflared/cert.pem"
  cat > "$home/.maxy-code/cloudflared/config.yml" <<YML
tunnel: test-tunnel-id
credentials-file: $home/.maxy-code/cloudflared/does-not-exist.json
ingress:
  - service: http_status:404
YML

  HOME="$home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then echo "  expected exit 0, got $rc"; return 1; fi
  echo "$log" | grep -q "op=resume-refused brand=maxy-code tunnel=test-tunnel-id reason=missing-run-credential" \
    || { echo "  missing resume-refused line"; echo "$log"; return 1; }
  echo "$log" | grep -q "spawned service=" \
    && { echo "  spawned despite no run credential"; echo "$log"; return 1; }
  return 0
}

# CASE J: corrupt tunnel.state → loud resume-refused, exits 0 (must never block
# the brand service). Exercises resume_refused on the invalid-state-json path,
# where TUNNEL_ID is not yet assigned — confirms the helper survives under set -u.
case_refuse_on_invalid_state_json() {
  local root; root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"
  local home="$root/home"
  echo 'this is not json {{{' > "$home/.maxy-code/cloudflared/tunnel.state"

  HOME="$home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME"
  local rc=$?
  local log; log=$(cat "$home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then echo "  expected exit 0 on corrupt state, got $rc"; return 1; fi
  echo "$log" | grep -q "op=resume-refused brand=maxy-code tunnel=unknown reason=invalid-state-json" \
    || { echo "  missing invalid-state-json resume-refused line"; echo "$log"; return 1; }
  echo "$log" | grep -q "spawned service=" \
    && { echo "  spawned on corrupt state"; echo "$log"; return 1; }
  return 0
}

# Task 1706 — the rotator's lock must be held across the offset-capture →
# verification-poll window. logs-rotate.sh truncates cloudflared.log in place
# when it exceeds 64 MiB; a rotation landing inside this window makes
# `tail -c "+$LOG_OFFSET"` read past EOF and report a tunnel failure that did
# not happen.
case_holds_rotate_lock_during_verify() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"

  local log_dir="$root/home/.maxy-code/logs"
  local marker="$root/lock-was-held"

  # Poll for the lock's existence while the script runs. The verify gate is the
  # window under test, so a single observation of the lock proves it was held
  # across it.
  ( for _ in $(seq 1 60); do
      [[ -d "$log_dir/.rotate.lock" ]] && { touch "$marker"; break; }
      sleep 0.05
    done ) &
  local watcher=$!

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME" >/dev/null 2>&1
  local rc=$?
  wait "$watcher" 2>/dev/null

  local held=0 leaked=0
  [[ -e "$marker" ]] && held=1
  [[ -d "$log_dir/.rotate.lock" ]] && leaked=1
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0, got $rc"; return 1
  fi
  if [[ $held -ne 1 ]]; then
    echo "  rotate lock was never held during the verify window"; return 1
  fi
  if [[ $leaked -ne 0 ]]; then
    echo "  rotate lock not released after the verify window"; return 1
  fi
  return 0
}

# A lock already held (by a live rotator tick) must not stop the tunnel coming
# up. A false-alarm verify line is a far smaller failure than no tunnel.
case_busy_rotate_lock_does_not_block_spawn() {
  local root
  root=$(mktemp -d /tmp/resume-tunnel-test-XXXXXX)
  setup "$root" ".maxy-code" "inactive" "yes"

  mkdir -p "$root/home/.maxy-code/logs/.rotate.lock"

  HOME="$root/home" \
  PATH="$root/bin:$PATH" \
  MAXY_PLATFORM_ROOT="$root/platform_root" \
    bash "$RESUME" >/dev/null 2>&1
  local rc=$?

  local log_content
  log_content=$(cat "$root/home/.maxy-code/logs/cloudflared.log" 2>/dev/null || echo "")
  cleanup "$root"

  if [[ $rc -ne 0 ]]; then
    echo "  expected exit 0, got $rc"; return 1
  fi
  if ! echo "$log_content" | grep -q "spawned service=cloudflared-maxy-code.service"; then
    echo "  a busy rotate lock blocked the tunnel spawn"; echo "$log_content"; return 1
  fi
  if ! echo "$log_content" | grep -q "rotate-lock busy"; then
    echo "  proceeding without the lock was not logged"; echo "$log_content"; return 1
  fi
  return 0
}

run_case "skip when service active" case_skip_when_scope_active
run_case "spawn succeeds and logs verified" case_spawn_succeeds
run_case "spawn with no connection logs ERROR exits 0" case_spawn_no_connection
run_case "service unit name derived from configDir" case_scope_unit_name_from_config_dir
run_case "slice flag passed to systemd-run" case_slice_flag_present
run_case "supervision contract: restart-always + no-autoupdate + .service" case_supervision_contract
run_case "reap: stray reaped then spawn (inactive)" case_reap_stray_then_spawn
run_case "reap: keeps supervised survivor, no spawn (active)" case_reap_keeps_survivor
run_case "reap: idempotent when single+active" case_reap_idempotent
run_case "reap: audit line names brand tunnel id" case_reap_audit_tunnel_id
run_case "reap: foreign co-resident connector untouched" case_reap_foreign_untouched
run_case "reap: SIGTERM survivor escalates to SIGKILL" case_reap_sigkill_escalation
run_case "reap: skip when active unit's MainPID is unresolved" case_reap_skip_when_mainpid_unresolved
run_case "token tunnel spawns without --origincert" case_token_tunnel_spawns_without_origincert
run_case "configured tunnel with no run credential refuses loudly" case_refuse_when_no_run_credential
run_case "corrupt tunnel.state refuses loudly and exits 0" case_refuse_on_invalid_state_json
run_case "holds the rotate lock during verify" case_holds_rotate_lock_during_verify
run_case "busy rotate lock does not block spawn" case_busy_rotate_lock_does_not_block_spawn

echo ""
echo "Results: $PASS passed, $FAIL failed"
[[ $FAIL -eq 0 ]]
