Files
fn_registry/functions/infra/tmux_map_claude_panes_test.go
T
egutierrez 927437a8d8 feat(infra): grupo claude-fleet — FleetView TUI + orquestacion de Claudes
Sistema FleetView para centralizar la flota de procesos Claude Code vivos en una
sola ventana kitty + tmux (socket aislado -L fleet) con un panel TUI:

- list_claude_fleet (+ tipo claude_fleet): escanea ~/.claude/sessions + goals +
  runtime, valida procesos vivos (anti-PID-reciclado), join por sessionId.
- list_resumable_claudes (+ tipo resumable_claude): sesiones cerradas reanudables.
- wrappers tmux: tmux_new_claude_window (con --resume), tmux_swap_window_into_console
  (preserva ancho del sidebar), tmux_map_claude_panes.
- launch_kittyclaude: comando entrypoint; instala atajos alt+flechas/enter/n/0/k/r,
  mouse on, remain-on-exit off; fija el ancho del sidebar con hooks.
- docs/capabilities/claude-fleet.md + entrada en el INDEX.

Incluye ademas funciones datascience en progreso (excel/duckdb/postgres) y ajustes
varios de docs e infra de otra sesion, agrupados aqui para no perderlos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 00:04:41 +02:00

103 lines
3.3 KiB
Go

//go:build !windows && linux
package infra
import (
"os"
"os/exec"
"strings"
"testing"
)
// TestTmuxMapClaudePanesNoClaude verifica que, sobre un servidor tmux aislado
// cuyos panes solo corren `cat` (no claude), el mapa devuelto esta vacio: ningun
// pane es ni tiene como hijo un proceso `claude`. Tambien valida que el comando
// list-panes -a se ejecuta sin error sobre el socket aislado.
func TestTmuxMapClaudePanesNoClaude(t *testing.T) {
tmuxAvailable(t)
socket := isolatedSocket(t)
session := "fleet"
startConsoleSession(t, socket, session)
newCatWindow(t, socket, session)
newCatWindow(t, socket, session)
m, err := TmuxMapClaudePanes(socket)
if err != nil {
t.Fatalf("TmuxMapClaudePanes: %v", err)
}
if len(m) != 0 {
t.Errorf("ningun pane corre claude, el mapa deberia estar vacio, tiene %d: %v", len(m), m)
}
}
func TestTmuxMapClaudePanesEmptySocket(t *testing.T) {
if _, err := TmuxMapClaudePanes(""); err == nil {
t.Error("socket vacio deberia dar error")
}
}
// TestProcCommSelf valida procComm contra el propio proceso de test: comm debe
// coincidir con el de /proc/self/comm (el binario de test, no "claude").
func TestProcCommSelf(t *testing.T) {
self := os.Getpid()
got := procComm(self)
if got == "" {
t.Fatalf("procComm(%d) devolvio vacio", self)
}
want := strings.TrimSpace(readSelfComm(t))
if got != want {
t.Errorf("procComm(%d) = %q, /proc/self/comm = %q", self, got, want)
}
}
func readSelfComm(t *testing.T) string {
t.Helper()
data, err := os.ReadFile("/proc/self/comm")
if err != nil {
t.Fatalf("read /proc/self/comm: %v", err)
}
return string(data)
}
// TestFindClaudePIDDetectsChild ejercita el mecanismo "¿este pid o hijo es
// claude?" SIN claude real: lanza un proceso hijo cuyo comm sea verificable y
// comprueba que (a) findClaudePID(propio pid) no lo confunde con claude, y (b)
// procChildren detecta al hijo lanzado. Testear con un proceso `claude` real es
// inviable en CI; este test valida el helper de deteccion con un comm conocido.
func TestFindClaudePIDDetectsChild(t *testing.T) {
// (a) El proceso de test NO es claude: findClaudePID no debe reportarlo.
if _, ok := findClaudePID(os.Getpid()); ok {
// Solo seria true si el binario de test se llamara "claude" (no es el caso).
t.Errorf("findClaudePID(self) reporto claude para un proceso que no lo es")
}
// (b) Lanzamos un hijo `sleep` (comm conocido "sleep") y verificamos que
// procChildren lo detecta como descendiente directo. Esto valida el
// mecanismo de barrido de hijos que findClaudePID usa internamente para
// localizar un comm objetivo (en produccion: "claude").
cmd := exec.Command("sleep", "3")
if err := cmd.Start(); err != nil {
t.Skipf("no se pudo lanzar sleep: %v", err)
}
childPID := cmd.Process.Pid
t.Cleanup(func() { _ = cmd.Process.Kill(); _ = cmd.Wait() })
kids := procChildren(os.Getpid())
found := false
for _, k := range kids {
if k == childPID {
found = true
break
}
}
if !found {
t.Errorf("procChildren(self) no incluyo al hijo %d; kids=%v", childPID, kids)
}
// Y el comm del hijo debe ser "sleep", confirmando el camino que findClaudePID
// usa para comparar contra "claude".
if comm := procComm(childPID); comm != "sleep" {
t.Errorf("procComm(%d) = %q, esperado \"sleep\"", childPID, comm)
}
}