Files
fn_registry/bash/functions/infra/kill_fleet_agent_test.sh
T
egutierrez f415dd56f5 feat(infra): kill_fleet_agent — cierre dirigido de un ejecutor de la flota (auto-kill)
Cierra UN ejecutor por sessionId (exacto/prefijo) o PID: SIGTERM al proceso claude
(cierre limpio, recuperable con --resume) + kill-window de su window tmux. Lo usa
el orquestador para liberar el slot idle de cada ejecutor en cuanto verifica que
su DoD-contrato esta met. Guards: NO mata a un role=orchestrator (leido del
goal.json) ni a la sesion que invoca (PID propio por ancestros /proc). --dry-run
para inspeccionar sin tocar nada. Overrides FN_FLEET_* para tests. Tag grupo
orchestration. Tests: 17 asserts (golden por sessionId/PID/prefijo, guards
orchestrator/self rc=3, errores rc=2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:28:07 +02:00

110 lines
4.0 KiB
Bash

#!/usr/bin/env bash
# Tests para kill_fleet_agent. Usa fixtures en dirs temporales (FN_FLEET_*) y
# --dry-run para no matar procesos ni cerrar windows reales.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/kill_fleet_agent.sh"
PASS=0
FAIL=0
assert_contains() {
local test_name="$1" needle="$2" haystack="$3"
if echo "$haystack" | grep -qF "$needle"; then
echo "PASS: $test_name"
PASS=$((PASS+1))
else
echo "FAIL: $test_name — expected to contain '$needle'"
echo " got: $haystack"
FAIL=$((FAIL+1))
fi
}
assert_rc() {
local test_name="$1" expected="$2" actual="$3"
if [[ "$actual" == "$expected" ]]; then
echo "PASS: $test_name (rc=$actual)"
PASS=$((PASS+1))
else
echo "FAIL: $test_name — expected rc=$expected, got rc=$actual"
FAIL=$((FAIL+1))
fi
}
# --- Fixtures: sessions/<pid>.json + goals/<sid>.json en dirs temporales ---
TMP="$(mktemp -d)"
SESS="$TMP/sessions"
GOALS="$TMP/goals"
mkdir -p "$SESS" "$GOALS"
# Ejecutor: PID 4242, sessionId executor-aaa-111, role=executor.
echo '{"sessionId":"executor-aaa-111","cwd":"/tmp/x"}' > "$SESS/4242.json"
echo '{"goal":"hacer X","role":"executor","dod_contract":"golden..."}' > "$GOALS/executor-aaa-111.json"
# Orquestador: PID 5555, sessionId orchestrator-bbb-222, role=orchestrator.
echo '{"sessionId":"orchestrator-bbb-222","cwd":"/tmp/y"}' > "$SESS/5555.json"
echo '{"goal":"orquestar","role":"orchestrator"}' > "$GOALS/orchestrator-bbb-222.json"
trap 'rm -rf "$TMP"' EXIT
export FN_FLEET_SESSIONS_DIR="$SESS"
export FN_FLEET_GOALS_DIR="$GOALS"
# Forzar self_pid a un valor que NO colisione con los fixtures (salvo el test self).
export FN_FLEET_SELF_PID=999999
# --- Test 1 (golden): resolver ejecutor por sessionId, dry-run imprime plan ---
set +e
out=$(kill_fleet_agent executor-aaa-111 --socket nope --dry-run 2>&1); rc=$?
set -e
assert_rc "golden: ejecutor por sessionId sale 0" 0 "$rc"
assert_contains "golden: plan muestra el PID resuelto" "PID: 4242" "$out"
assert_contains "golden: plan muestra el sessionId" "executor-aaa-111" "$out"
assert_contains "golden: plan muestra role executor" "role: executor" "$out"
assert_contains "golden: dry-run no mata" "DRY-RUN" "$out"
# --- Test 2 (golden por PID + prefijo de sessionId) ---
set +e
out=$(kill_fleet_agent 4242 --dry-run 2>&1); rc=$?
set -e
assert_rc "golden: target por PID sale 0" 0 "$rc"
assert_contains "golden: PID resuelve su sessionId" "executor-aaa-111" "$out"
set +e
out=$(kill_fleet_agent executor-aaa --dry-run 2>&1); rc=$?
set -e
assert_rc "edge: prefijo de sessionId resuelve" 0 "$rc"
assert_contains "edge: prefijo resuelve al PID 4242" "PID: 4242" "$out"
# --- Test 3 (EDGE guard role): negar matar a un orchestrator ---
set +e
out=$(kill_fleet_agent orchestrator-bbb-222 --dry-run 2>&1); rc=$?
set -e
assert_rc "guard: matar orchestrator devuelve rc=3" 3 "$rc"
assert_contains "guard: mensaje menciona role=orchestrator" "role=orchestrator" "$out"
# --- Test 4 (EDGE guard self): negar matar a la sesion actual ---
set +e
out=$(FN_FLEET_SELF_PID=4242 kill_fleet_agent executor-aaa-111 --dry-run 2>&1); rc=$?
set -e
assert_rc "guard: matar self devuelve rc=3" 3 "$rc"
assert_contains "guard: mensaje self menciona no suicidarse" "No me suicido" "$out"
# --- Test 5 (ERROR): target no resuelto a un PID ---
set +e
out=$(kill_fleet_agent sesion-inexistente-zzz --dry-run 2>&1); rc=$?
set -e
assert_rc "error: target inexistente devuelve rc=2" 2 "$rc"
assert_contains "error: mensaje de no resuelto" "no se pudo resolver" "$out"
# --- Test 6 (ERROR): falta el target ---
set +e
out=$(kill_fleet_agent --dry-run 2>&1); rc=$?
set -e
assert_rc "error: sin target devuelve rc=2" 2 "$rc"
assert_contains "error: mensaje falta target" "falta el target" "$out"
echo "---"
echo "Results: $PASS passed, $FAIL failed"
[[ $FAIL -eq 0 ]] || exit 1