//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) } }