package main import ( "os/exec" "testing" "time" ) func TestNewPool(t *testing.T) { p := NewPool() if p == nil { t.Fatal("NewPool returned nil") } } func TestGetEmpty(t *testing.T) { p := NewPool() s := p.Get("nonexistent") if s != nil { t.Fatal("Get on empty pool should return nil") } } func TestIsAliveEmpty(t *testing.T) { p := NewPool() if p.IsAlive("nonexistent") { t.Fatal("IsAlive on empty pool should return false") } } // setupTmuxSession creates a tmux session for testing. Returns a cleanup function. func setupTmuxSession(t *testing.T, name string) func() { t.Helper() // Kill any leftover session with this name exec.Command("tmux", "kill-session", "-t", name).Run() cmd := exec.Command("tmux", "new-session", "-d", "-s", name, "-x", "80", "-y", "24") if err := cmd.Run(); err != nil { t.Fatalf("failed to create tmux session %q: %v", name, err) } return func() { exec.Command("tmux", "kill-session", "-t", name).Run() } } func TestAttachCreatesSession(t *testing.T) { name := "test-pool-attach" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() sess, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Attach failed: %v", err) } if sess == nil { t.Fatal("Attach returned nil session") } if sess.PTY == nil { t.Fatal("Session PTY is nil") } if sess.Cmd == nil { t.Fatal("Session Cmd is nil") } if sess.Cols != 80 { t.Errorf("Cols = %d, want 80", sess.Cols) } if sess.Rows != 24 { t.Errorf("Rows = %d, want 24", sess.Rows) } } func TestGetReturnsSession(t *testing.T) { name := "test-pool-get" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() _, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Attach failed: %v", err) } sess := p.Get(name) if sess == nil { t.Fatal("Get returned nil after Attach") } } func TestIsAliveAfterAttach(t *testing.T) { name := "test-pool-alive" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() _, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Attach failed: %v", err) } if !p.IsAlive(name) { t.Fatal("IsAlive should return true after Attach") } } func TestAttachReusesLiveSession(t *testing.T) { name := "test-pool-reuse" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() sess1, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("First Attach failed: %v", err) } sess2, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Second Attach failed: %v", err) } if sess1 != sess2 { t.Fatal("Attach should reuse existing live session") } } func TestResize(t *testing.T) { name := "test-pool-resize" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() _, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Attach failed: %v", err) } err = p.Resize(name, 120, 40) if err != nil { t.Fatalf("Resize failed: %v", err) } sess := p.Get(name) if sess.Cols != 120 { t.Errorf("After resize Cols = %d, want 120", sess.Cols) } if sess.Rows != 40 { t.Errorf("After resize Rows = %d, want 40", sess.Rows) } } func TestResizeNonexistent(t *testing.T) { p := NewPool() err := p.Resize("nonexistent", 80, 24) if err == nil { t.Fatal("Resize on nonexistent session should return error") } } func TestCloseAll(t *testing.T) { name := "test-pool-closeall" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() _, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Attach failed: %v", err) } p.CloseAll() // Give the background goroutine time to clean up time.Sleep(100 * time.Millisecond) if p.IsAlive(name) { t.Fatal("IsAlive should return false after CloseAll") } } func TestAttachCleansUpDeadSession(t *testing.T) { name := "test-pool-dead-cleanup" cleanup := setupTmuxSession(t, name) defer cleanup() p := NewPool() defer p.CloseAll() sess1, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("First Attach failed: %v", err) } // Kill the process to simulate a dead session sess1.Cmd.Process.Kill() sess1.Cmd.Process.Wait() time.Sleep(100 * time.Millisecond) // Re-create the tmux session since killing the attach killed our handle exec.Command("tmux", "kill-session", "-t", name).Run() setupCmd := exec.Command("tmux", "new-session", "-d", "-s", name, "-x", "80", "-y", "24") if err := setupCmd.Run(); err != nil { t.Fatalf("failed to recreate tmux session: %v", err) } sess2, err := p.Attach(name, 80, 24) if err != nil { t.Fatalf("Second Attach after dead session failed: %v", err) } if sess1 == sess2 { t.Fatal("Attach should create new session when old one is dead") } }