927437a8d8
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>
103 lines
3.3 KiB
Go
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)
|
|
}
|
|
}
|