ollie/cmd/olliesrv/internal/session/session_test.go

252 lines
6.5 KiB
Go

package session
import (
"context"
"fmt"
"os"
"testing"
"time"
"ollie/toolsrv/protocol"
)
func TestSetCwd_ExpandsTilde(t *testing.T) {
home, err := os.UserHomeDir()
if err != nil || home == "" {
t.Skip("cannot determine home directory")
}
s := &Session{}
s.SetCwd("~/projects")
want := home + "/projects"
if got := s.Cwd(); got != want {
t.Errorf("SetCwd(~/projects): got %q, want %q", got, want)
}
}
func TestSetCwd_ExpandsEnvVars(t *testing.T) {
t.Setenv("OLLIE_TEST_DIR", "/tmp/testdir")
s := &Session{}
s.SetCwd("$OLLIE_TEST_DIR/sub")
want := "/tmp/testdir/sub"
if got := s.Cwd(); got != want {
t.Errorf("SetCwd($OLLIE_TEST_DIR/sub): got %q, want %q", got, want)
}
}
func TestSetCwd_AbsolutePathUnchanged(t *testing.T) {
s := &Session{}
s.SetCwd("/usr/local/bin")
want := "/usr/local/bin"
if got := s.Cwd(); got != want {
t.Errorf("SetCwd(/usr/local/bin): got %q, want %q", got, want)
}
}
func TestEventSubscription(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch := SubscribeEvents(ctx)
// Give subscription time to register
time.Sleep(10 * time.Millisecond)
// Publish events
for i := 0; i < 5; i++ {
PublishEvent(fmt.Sprintf("test.event.%d", i), fmt.Sprintf("payload%d", i))
}
// Collect events
timeout := time.After(1 * time.Second)
var received []Event
for {
select {
case ev := <-ch:
received = append(received, ev)
if len(received) >= 5 {
goto done
}
case <-timeout:
goto done
}
}
done:
if len(received) < 5 {
t.Errorf("Expected 5 events, got %d", len(received))
for i, ev := range received {
t.Logf(" [%d] topic=%s payload=%s", i, ev.Topic, ev.Payload)
}
}
}
func TestEventSubscriptionFiltered(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch := SubscribeEventsFiltered(ctx, "*")
// Give subscription time to register
time.Sleep(10 * time.Millisecond)
// Publish events
for i := 0; i < 5; i++ {
PublishEvent(fmt.Sprintf("test.event.%d", i), fmt.Sprintf("payload%d", i))
}
// Collect events
timeout := time.After(1 * time.Second)
var received []Event
for {
select {
case ev := <-ch:
received = append(received, ev)
if len(received) >= 5 {
goto done
}
case <-timeout:
goto done
}
}
done:
if len(received) < 5 {
t.Errorf("Expected 5 events, got %d", len(received))
for i, ev := range received {
t.Logf(" [%d] topic=%s payload=%s", i, ev.Topic, ev.Payload)
}
}
}
func TestEventSubscriptionRapidFire(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch := SubscribeEventsFiltered(ctx, "*")
// Give subscription time to register
time.Sleep(50 * time.Millisecond)
// Rapid-fire publish (simulating state changes during a turn)
states := []string{"thinking", "calling: shell", "thinking", "calling: file_read", "thinking", "idle"}
for _, state := range states {
PublishEvent("session.abc.agent.xyz.state", state)
}
// Collect events
timeout := time.After(2 * time.Second)
var received []Event
for {
select {
case ev := <-ch:
received = append(received, ev)
t.Logf("Received: topic=%s payload=%s", ev.Topic, ev.Payload)
if len(received) >= len(states) {
goto done
}
case <-timeout:
goto done
}
}
done:
if len(received) != len(states) {
t.Errorf("Expected %d events, got %d", len(states), len(received))
}
for i, ev := range received {
if i < len(states) && ev.Payload != states[i] {
t.Errorf("Event %d: expected payload %q, got %q", i, states[i], ev.Payload)
}
}
}
// --- Bypass request tracking tests ---
func TestBypassPending_MultipleConcurrent(t *testing.T) {
s := &Session{ID: "test-session"}
// Add multiple bypass requests
req1 := &protocol.BypassRequest{ID: "req-1", Cmd: "rm -rf /tmp/a", Cwd: "/home/user", Env: map[string]string{"OLLIE_UNAME": "agent1"}}
req2 := &protocol.BypassRequest{ID: "req-2", Cmd: "rm -rf /tmp/b", Cwd: "/home/user", Env: map[string]string{"OLLIE_UNAME": "agent2"}}
req3 := &protocol.BypassRequest{ID: "req-3", Cmd: "rm -rf /tmp/c", Cwd: "/home/user", Env: map[string]string{"OLLIE_UNAME": "agent1"}}
s.SetBypassPending(req1, nil)
s.SetBypassPending(req2, nil)
s.SetBypassPending(req3, nil)
// Should have all three pending
pending := s.BypassPending()
if len(pending) != 3 {
t.Errorf("Expected 3 pending requests, got %d", len(pending))
}
// BypassPendingByID should find each
if got := s.BypassPendingByID("req-1"); got == nil || got.Cmd != "rm -rf /tmp/a" {
t.Errorf("BypassPendingByID(req-1) failed")
}
if got := s.BypassPendingByID("req-2"); got == nil || got.Cmd != "rm -rf /tmp/b" {
t.Errorf("BypassPendingByID(req-2) failed")
}
if got := s.BypassPendingByID("req-3"); got == nil || got.Cmd != "rm -rf /tmp/c" {
t.Errorf("BypassPendingByID(req-3) failed")
}
// Non-existent ID should return nil
if got := s.BypassPendingByID("req-999"); got != nil {
t.Errorf("BypassPendingByID(req-999) should return nil, got %v", got)
}
}
func TestBypassResolve_RemovesFromMap(t *testing.T) {
s := &Session{ID: "test-session"}
req1 := &protocol.BypassRequest{ID: "req-1", Cmd: "cmd1", Env: map[string]string{"OLLIE_UNAME": "agent1"}}
req2 := &protocol.BypassRequest{ID: "req-2", Cmd: "cmd2", Env: map[string]string{"OLLIE_UNAME": "agent2"}}
s.SetBypassPending(req1, nil)
s.SetBypassPending(req2, nil)
// Resolve req-1 (will fail on toolsrv connection, but map update should succeed)
err := s.ResolveBypass("req-1", true)
// Error expected because no dialFn, but the request should be removed
if err == nil {
t.Log("ResolveBypass succeeded (unexpected but ok for this test)")
}
// req-1 should be gone, req-2 should remain
pending := s.BypassPending()
if len(pending) != 1 {
t.Errorf("Expected 1 pending request after resolve, got %d", len(pending))
}
if s.BypassPendingByID("req-1") != nil {
t.Errorf("req-1 should be removed after resolve")
}
if s.BypassPendingByID("req-2") == nil {
t.Errorf("req-2 should still be pending")
}
}
func TestBypassResolve_NonExistent(t *testing.T) {
s := &Session{ID: "test-session"}
err := s.ResolveBypass("nonexistent", true)
if err == nil {
t.Errorf("ResolveBypass should fail for non-existent ID")
}
}
func TestBypassPending_Empty(t *testing.T) {
s := &Session{ID: "test-session"}
pending := s.BypassPending()
if len(pending) != 0 {
t.Errorf("Expected 0 pending requests for new session, got %d", len(pending))
}
if s.BypassPendingByID("any") != nil {
t.Errorf("BypassPendingByID should return nil for new session")
}
}