#!/usr/bin/env bats
# Checks written for SantanderAI/ralph by an unattended-agent review.
# Drop this file next to tests/ralph-loop.bats and run it with `bats tests`.
#
# Like the existing suite, nothing here needs codex, claude, gemini or devin to
# be installed: every test substitutes a fake tool binary through
# RALPH_CODEX_COMMAND. Every test fails on commit 0b710b2 and describes the
# behaviour the loop should have instead.

setup() {
  SCRIPT="${BATS_TEST_DIRNAME}/../ralph-loop.sh"
  WORK="$(mktemp -d)"
  FAKE="$WORK/faketool"
  cat > "$FAKE" <<'EOF'
#!/usr/bin/env bash
cat >/dev/null
echo "faketool $*"
exit "${FAKETOOL_EXIT:-0}"
EOF
  chmod +x "$FAKE"
  cd "$WORK"
}

teardown() {
  cd /
  pkill -f "$WORK" 2>/dev/null || true
  rm -rf "$WORK"
}

# Write a .ralph/.env into $1 that runs the fake tool and nothing else.
write_env() {
  local dir=$1
  mkdir -p "$dir/.ralph"
  {
    echo 'RALPH_TOOL=codex'
    echo 'RALPH_MODEL_CAPABILITY=med'
    echo 'RALPH_THINKING=false'
    echo 'RALPH_SWITCH_ON_EXHAUSTION=false'
    echo 'RALPH_MEMORY_MAX='
    echo "RALPH_CODEX_COMMAND=$FAKE"
    echo 'RALPH_CODEX_FLAGS="--noop"'
    if [ -n "${2:-}" ]; then echo "RALPH_LOOP_MAX_LOGS=$2"; fi
  } > "$dir/.ralph/.env"
  echo "do a thing" > "$dir/p.md"
}

@test "a run in which every iteration failed exits non-zero" {
  # Finding 3. The loop currently ends on an echo, so its exit status is 0 no
  # matter how many iterations failed. Any cron entry, CI job or systemd unit
  # wrapping it reads success from a run that did nothing.
  write_env "$WORK"
  FAKETOOL_EXIT=3 run bash "$SCRIPT" 3 p.md
  [[ "$output" == *"failed=3"* ]]
  [ "$status" -ne 0 ]
}

@test "log rotation still trims when the workspace path contains a space" {
  # Finding 2a. rotate_logs pipes ls into xargs, which splits on whitespace,
  # so every path is torn in two and no log is ever removed.
  ws="$WORK/my ws"
  mkdir -p "$ws"
  write_env "$ws" 2
  cd "$ws"
  run bash "$SCRIPT" 5 p.md
  [ "$(find "$ws/.ralph/logs" -name '*.log' | wc -l)" -eq 2 ]
}

@test "log rotation never removes a file outside the log directory" {
  # Finding 2b. The torn path is passed to `rm -f --`, so the fragment before
  # the space is deleted if anything happens to live there. rm -f is silent
  # about the fragment after it, so nothing is printed either way.
  ws="$WORK/my ws"
  mkdir -p "$ws"
  write_env "$ws" 1
  : > "$WORK/my"            # an unrelated file at the truncated path
  cd "$ws"
  run bash "$SCRIPT" 3 p.md
  [ -e "$WORK/my" ]
}

@test "SIGTERM stops the loop before the next iteration starts" {
  # Finding 1. trap ... EXIT HUP INT TERM installs a handler that returns, so
  # bash resumes the loop: kill and Ctrl-C end the current agent and the loop
  # immediately starts a new one. Only stop.md or SIGKILL actually stop it.
  cat > "$FAKE" <<'EOF'
#!/usr/bin/env bash
cat >/dev/null
sleep 6
EOF
  chmod +x "$FAKE"
  write_env "$WORK"
  out="$WORK/out.txt"
  : > "$out"
  # `trap - INT TERM QUIT` restores the default dispositions that a background
  # job of a non-interactive shell would otherwise inherit as "ignored".
  setsid bash -c "trap - INT TERM QUIT; exec bash '$SCRIPT' 4 p.md" > "$out" 2>&1 &
  sleep 2
  pid=$(pgrep -f "$SCRIPT 4 p.md" | tail -1)
  [ -n "$pid" ]
  kill -TERM "$pid"
  sleep 12
  run pgrep -f "$SCRIPT 4 p.md"
  [ "$status" -ne 0 ]                       # the loop is gone
  [ "$(grep -c '=== Iteration' "$out")" -le 1 ]   # and it did not start another
}

@test "the committed .ralph/.env matches what write_default_env_file writes" {
  # Finding 9. The repository ships a .ralph/.env that predates the devin
  # rotation: it has no RALPH_DEVIN_* block and its comment documents a
  # three-tool rotation. Because ralph-loop.sh only writes the defaults when
  # the file is absent, every clone keeps the stale copy forever.
  repo="${BATS_TEST_DIRNAME}/.."
  [ -f "$repo/.ralph/.env" ]
  extracted="$WORK/expected.env"
  sed -n "/^write_default_env_file() {$/,/^}$/p" "$repo/ralph-loop.sh" \
    | sed -e '1,2d' | sed -e '$d' | sed -e '$d' > "$extracted"
  run diff -u "$extracted" "$repo/.ralph/.env"
  [ "$status" -eq 0 ]
}
