#!/usr/bin/env bash
# Tests for bin/_human-session: the protected slice, the environment handover, and survival
# of the terminal that started the session.
#
# Exercised with a STUB runtime — no real agent runtime is started, and nothing this script
# did not create is ever signalled.
#
# Run: bash human-session.test.sh (exit 0 = all pass).
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
LAUNCHER="$ROOT/bin/_human-session"
PASS=0; FAIL=0
ok()  { PASS=$((PASS+1)); printf 'PASS %s\n' "$1"; }
bad() { FAIL=$((FAIL+1)); printf 'FAIL %s\n     %s\n' "$1" "$2"; }

UID_NUM="$(id -u)"
HUMAN_CG="/user.slice/user-${UID_NUM}.slice/user@${UID_NUM}.service/human.slice"
SOCK="$HOME/.local/state/human-session/tmux.sock"

# Outside /tmp: the runtime runs under tmpjail, whose overlay would swallow these files.
TMP=$(mktemp -d "$HOME/.cache/human-session-test-XXXX")
STUB="$TMP/stub-runtime"
cat >"$STUB" <<'STUB'
#!/usr/bin/env bash
awk -F: '$1=="0"{print $3}' /proc/self/cgroup >"$HS_OUT/cgroup"
cat /proc/self/oom_score_adj >"$HS_OUT/oom"
printf '%s' "${HS_TOKEN:-MISSING}" >"$HS_OUT/token"
printf '%s' "$*" >"$HS_OUT/argv"
printf '%s' "$$" >"$HS_OUT/pid"
sleep 300
STUB
chmod +x "$STUB"
mkdir -p "$TMP/out"

CLIENT_PID=""
SESSION=""
cleanup() {
  [[ -n "$SESSION" ]] && tmux -S "$SOCK" kill-session -t "$SESSION" 2>/dev/null
  [[ -n "$CLIENT_PID" ]] && kill "$CLIENT_PID" 2>/dev/null
  rm -rf "$TMP"
}
trap cleanup EXIT

before="$(tmux -S "$SOCK" list-sessions -F '#{session_name}' 2>/dev/null | wc -l)"

LEDGER="$TMP/ledger"
LEDGER_ID="human-test-$$"
mkdir -p "$LEDGER/sessions"
printf '{"schemaVersion":1,"ledgerId":"%s","runtime":"claude","cwd":"%s","startedAt":"2026-01-01T00:00:00Z"}\n' \
  "$LEDGER_ID" "$TMP" >"$LEDGER/sessions/$LEDGER_ID.json"

script -qec "env HS_OUT=$TMP/out HS_TOKEN=handover-ok AGENT_SESSIONS_DIR=$LEDGER AGENT_LEDGER_ID=$LEDGER_ID $LAUNCHER $STUB --flag 'two words'" /dev/null \
  >"$TMP/client.log" 2>&1 &
CLIENT_PID=$!

for _ in $(seq 1 100); do
  [[ -s "$TMP/out/cgroup" ]] && break
  sleep 0.2
done

if [[ ! -s "$TMP/out/cgroup" ]]; then
  bad "session starts" "stub never ran; client log: $(head -c 400 "$TMP/client.log")"
  printf 'human-session: pass=%d fail=%d\n' "$PASS" "$FAIL"
  exit 1
fi
ok "session starts and the runtime runs"

CG="$(cat "$TMP/out/cgroup")"
if [[ "$CG" == "$HUMAN_CG"/* ]]; then
  ok "runtime runs inside human.slice ($CG)"
else
  bad "runtime runs inside human.slice" "cgroup=$CG"
fi

OOM="$(cat "$TMP/out/oom" 2>/dev/null)"
if [[ "$OOM" == "-900" ]]; then
  ok "runtime inherits oom_score_adj=-900"
else
  bad "runtime inherits oom_score_adj=-900" "got '$OOM' (root via deck-sudo is required to lower it)"
fi

if [[ "$(cat "$TMP/out/token" 2>/dev/null)" == "handover-ok" ]]; then
  ok "launching shell's environment reaches the runtime"
else
  bad "launching shell's environment reaches the runtime" "token='$(cat "$TMP/out/token" 2>/dev/null)'"
fi

if [[ "$(cat "$TMP/out/argv" 2>/dev/null)" == "--flag two words" ]]; then
  ok "argv passthrough is exact"
else
  bad "argv passthrough is exact" "argv='$(cat "$TMP/out/argv" 2>/dev/null)'"
fi

# The stub reports its own pid: pgrep would match the client, whose argv also names the stub.
STUB_PID="$(cat "$TMP/out/pid" 2>/dev/null)"

# The stub runs before the launcher attaches the client, so the session is briefly unattached.
for _ in $(seq 1 100); do
  SESSION="$(tmux -S "$SOCK" list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null |
    awk '$2 == 1 {print $1}' | tail -1)"
  [[ -n "$SESSION" ]] && break
  sleep 0.2
done
if [[ -n "$SESSION" ]]; then
  ok "an attached tmux session exists ($SESSION)"
else
  bad "an attached tmux session exists" "$(tmux -S "$SOCK" list-sessions 2>&1 | head -3)"
fi

ENTRY="$LEDGER/sessions/$LEDGER_ID.json"
if grep -q "\"tmuxSession\": \"$SESSION\"" "$ENTRY" && grep -q "\"tmuxSocket\": \"$SOCK\"" "$ENTRY"; then
  ok "the ledger entry records the tmux target the session can be reopened through"
else
  bad "the ledger entry records the tmux target the session can be reopened through" "$(cat "$ENTRY")"
fi

# The entry carries no dtach mux, so this line can only come from the tmux target being probed live.
if timeout 60 env AGENT_SESSIONS_DIR="$LEDGER" "$ROOT/bin/agent-sessions" 2>&1 |
   grep -q "agent-sessions attach $LEDGER_ID"; then
  ok "a live tmux-hosted session is listed as reopenable"
else
  bad "a live tmux-hosted session is listed as reopenable" \
    "$(timeout 60 env AGENT_SESSIONS_DIR="$LEDGER" "$ROOT/bin/agent-sessions" 2>&1 | head -12)"
fi

# Without a terminal tmux itself refuses; what is under test is that agent-sessions routes to it
# at all instead of declaring a live tmux-hosted session dead.
if timeout 20 env AGENT_SESSIONS_DIR="$LEDGER" "$ROOT/bin/agent-sessions" attach "$LEDGER_ID" \
   </dev/null 2>&1 | grep -q "not running any more"; then
  bad "agent-sessions can reopen a tmux-hosted session" "attach refused a live tmux target"
else
  ok "agent-sessions can reopen a tmux-hosted session"
fi

after="$(tmux -S "$SOCK" list-sessions -F '#{session_name}' 2>/dev/null | wc -l)"
if (( after == before + 1 )); then
  ok "exactly one session was added"
else
  bad "exactly one session was added" "before=$before after=$after"
fi

# Terminal death: kill the client the way a gnome-terminal-server crash would.
kill -9 "$CLIENT_PID" 2>/dev/null
CLIENT_PID=""
sleep 2

if [[ -n "$STUB_PID" ]] && kill -0 "$STUB_PID" 2>/dev/null; then
  ok "runtime survives the terminal being killed (pid $STUB_PID)"
else
  bad "runtime survives the terminal being killed" "stub pid '$STUB_PID' is gone"
fi

if tmux -S "$SOCK" list-sessions -F '#{session_name} #{session_attached}' 2>/dev/null |
   awk -v s="$SESSION" '$1 == s && $2 == 0 {found=1} END {exit !found}'; then
  ok "session is now detached and reattachable"
else
  bad "session is now detached and reattachable" "$(tmux -S "$SOCK" list-sessions 2>&1 | head -3)"
fi

if "$ROOT/bin/claude-sessions" list 2>&1 | grep -q "$SESSION"; then
  ok "claude-sessions lists the surviving session"
else
  bad "claude-sessions lists the surviving session" "$("$ROOT/bin/claude-sessions" list 2>&1 | head -5)"
fi

if [[ -z "$(ls -A "$HOME/.local/state/human-session"/spawn.* 2>/dev/null)" ]]; then
  ok "spawn files carrying the environment are unlinked"
else
  bad "spawn files carrying the environment are unlinked" "$(ls -l "$HOME/.local/state/human-session"/spawn.* 2>&1 | head -3)"
fi

printf 'human-session: pass=%d fail=%d\n' "$PASS" "$FAIL"
(( FAIL == 0 ))
