Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
133 lines
3.3 KiB
Go
133 lines
3.3 KiB
Go
package agentruntime
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEventHubScopesEventsByRoom(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewEventHub()
|
|
roomAEvents, unsubscribeRoomA := hub.Subscribe("room-a", "session-1")
|
|
defer unsubscribeRoomA()
|
|
roomBEvents, unsubscribeRoomB := hub.Subscribe("room-b", "session-1")
|
|
defer unsubscribeRoomB()
|
|
|
|
hub.Publish("room-a", "session-1", []StreamEvent{{
|
|
EventType: StreamEventMessageUpdate,
|
|
Data: map[string]any{"text": "room a"},
|
|
}})
|
|
|
|
select {
|
|
case event := <-roomAEvents:
|
|
data := event.Data.(map[string]any)
|
|
if data["text"] != "room a" {
|
|
t.Fatalf("room-a event data = %#v, want room a", event.Data)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("expected room-a subscriber to receive event")
|
|
}
|
|
|
|
select {
|
|
case event := <-roomBEvents:
|
|
t.Fatalf("room-b subscriber received cross-room event: %#v", event)
|
|
case <-time.After(50 * time.Millisecond):
|
|
}
|
|
|
|
hub.Publish("room-b", "session-1", []StreamEvent{{
|
|
EventType: StreamEventMessageUpdate,
|
|
Data: map[string]any{"text": "room b"},
|
|
}})
|
|
|
|
select {
|
|
case event := <-roomBEvents:
|
|
data := event.Data.(map[string]any)
|
|
if data["text"] != "room b" {
|
|
t.Fatalf("room-b event data = %#v, want room b", event.Data)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("expected room-b subscriber to receive event")
|
|
}
|
|
}
|
|
|
|
func TestEventHubQueuesSubscriberBackpressureWithoutDropping(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewEventHub()
|
|
events, unsubscribe := hub.Subscribe("room-a", "session-1")
|
|
defer unsubscribe()
|
|
|
|
const eventCount = 96
|
|
published := make([]StreamEvent, 0, eventCount)
|
|
for i := range eventCount {
|
|
published = append(published, StreamEvent{
|
|
EventType: StreamEventMessageUpdate,
|
|
Data: map[string]any{"index": i},
|
|
})
|
|
}
|
|
|
|
hub.Publish("room-a", "session-1", published)
|
|
|
|
for i := range eventCount {
|
|
select {
|
|
case event := <-events:
|
|
data := event.Data.(map[string]any)
|
|
if data["index"] != i {
|
|
t.Fatalf("event index = %#v, want %d", data["index"], i)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatalf("timed out waiting for event %d", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEventHubReconcilesAndClosesOverflowedSubscriber(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
hub := NewEventHub()
|
|
events, unsubscribe := hub.Subscribe("room-a", "session-1")
|
|
defer unsubscribe()
|
|
|
|
published := make([]StreamEvent, 0, eventHubSubscriberQueueLimit+128)
|
|
for i := 0; i < eventHubSubscriberQueueLimit+128; i++ {
|
|
published = append(published, StreamEvent{
|
|
EventType: StreamEventMessageDelta,
|
|
Data: map[string]any{"index": i},
|
|
})
|
|
}
|
|
hub.Publish("room-a", "session-1", published)
|
|
|
|
for {
|
|
select {
|
|
case event, ok := <-events:
|
|
if !ok {
|
|
t.Fatal("overflowed subscriber closed before reconcile event")
|
|
}
|
|
if event.EventType != StreamEventSessionReconcileRequired {
|
|
continue
|
|
}
|
|
data, ok := event.Data.(map[string]any)
|
|
if !ok || data["reason"] != "event_hub_queue_overflow" {
|
|
t.Fatalf("reconcile event data = %#v", event.Data)
|
|
}
|
|
if data["agentSessionId"] != "session-1" {
|
|
t.Fatalf("reconcile event session = %#v", data["agentSessionId"])
|
|
}
|
|
goto reconciled
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timed out waiting for reconcile event")
|
|
}
|
|
}
|
|
|
|
reconciled:
|
|
select {
|
|
case _, ok := <-events:
|
|
if ok {
|
|
t.Fatal("overflowed subscriber emitted events after reconcile")
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("overflowed subscriber did not close")
|
|
}
|
|
}
|