ollie/toolsrv/socket_test.go

165 lines
3.6 KiB
Go

package toolsrv_test
import (
"context"
"net"
"os"
"path/filepath"
"testing"
"time"
"ollie/toolsrv"
)
// TestSocketSpawnAndDial verifies the full lifecycle:
// spawn ollie-remote → dial the socket → call RPCs → close.
func TestSocketSpawnAndDial(t *testing.T) {
// Skip if ollie-remote binary not available.
if _, err := findBinary(); err != nil {
t.Skipf("ollie-remote not found: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
// Spawn the toolsrv subprocess.
proc, err := toolsrv.Spawn(ctx, cwd, toolsrv.WithYolo())
if err != nil {
t.Fatalf("Spawn: %v", err)
}
defer proc.Close()
if proc.Socket == "" {
t.Fatal("expected non-empty socket path")
}
// Verify socket file exists.
if _, err := os.Stat(proc.Socket); err != nil {
t.Fatalf("socket file not found: %v", err)
}
// Dial a connection.
conn, err := toolsrv.Dial(ctx, toolsrv.LocalAddr(proc.Socket))
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
// Ping.
if err := conn.Ping(); err != nil {
t.Fatalf("Ping: %v", err)
}
// FetchHostInfo.
info, err := conn.FetchHostInfo()
if err != nil {
t.Fatalf("FetchHostInfo: %v", err)
}
if info.Platform == "" {
t.Error("expected non-empty platform")
}
// SetEnv (fire-and-forget, just verify no crash).
conn.SetEnv("TEST_KEY", "test_value")
// SetCWD.
conn.SetCWD("/tmp")
// ListTools. The remote server starts with no promoted tools; tools are
// loaded dynamically through the session tool registry.
if _, err := conn.ListTools(); err != nil {
t.Fatalf("ListTools: %v", err)
}
}
// TestSocketMultipleClients verifies multiple clients can connect simultaneously.
func TestSocketMultipleClients(t *testing.T) {
if _, err := findBinary(); err != nil {
t.Skipf("ollie-remote not found: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
proc, err := toolsrv.Spawn(ctx, cwd, toolsrv.WithYolo())
if err != nil {
t.Fatalf("Spawn: %v", err)
}
defer proc.Close()
// Dial three clients concurrently.
conns := make([]*toolsrv.Conn, 3)
for i := range conns {
c, err := toolsrv.Dial(ctx, toolsrv.LocalAddr(proc.Socket))
if err != nil {
t.Fatalf("Dial[%d]: %v", i, err)
}
conns[i] = c
}
// Each client can ping independently.
for i, c := range conns {
if err := c.Ping(); err != nil {
t.Errorf("Ping[%d]: %v", i, err)
}
}
// Close all.
for _, c := range conns {
c.Close()
}
}
// TestSocketCleanup verifies the socket is removed after Close.
func TestSocketCleanup(t *testing.T) {
if _, err := findBinary(); err != nil {
t.Skipf("ollie-remote not found: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
proc, err := toolsrv.Spawn(ctx, cwd, toolsrv.WithYolo())
if err != nil {
t.Fatalf("Spawn: %v", err)
}
sockPath := proc.Socket
proc.Close()
// Socket should be cleaned up.
time.Sleep(100 * time.Millisecond)
if _, err := net.DialTimeout("unix", sockPath, time.Second); err == nil {
t.Error("expected socket to be removed after Close")
}
}
// findBinary checks if ollie-remote is available.
func findBinary() (string, error) {
home, _ := os.UserHomeDir()
candidates := []string{
filepath.Join(home, "bin", "ollie-remote"),
filepath.Join(home, ".local", "bin", "ollie-remote"),
}
for _, p := range candidates {
if _, err := os.Stat(p); err == nil {
return p, nil
}
}
return "", os.ErrNotExist
}