package agentruntime import ( "context" "sync" "testing" ) type eventRecorder struct { events []RuntimeEvent } func (r *eventRecorder) Emit(_ context.Context, event RuntimeEvent) error { r.events = append(r.events, event) return nil } func TestTerminalEmitterForwardsExactlyOneTerminal(t *testing.T) { recorder := &eventRecorder{} emitter := NewTerminalEmitter(recorder) events := []RuntimeEvent{ {RunID: "run-1", Type: EventTypeStart}, {RunID: "run-1", Type: EventTypeDelta, Delta: "ok"}, {RunID: "run-1", Type: EventTypeComplete}, {RunID: "run-1", Type: EventTypeError, Error: "late"}, {RunID: "run-1", Type: EventTypeDelta, Delta: "late"}, } for _, event := range events { if err := emitter.Emit(context.Background(), event); err != nil { t.Fatalf("Emit() error = %v", err) } } if !emitter.TerminalObserved() { t.Fatal("terminal was not observed") } if len(recorder.events) != 3 { t.Fatalf("event count = %d, want 3: %#v", len(recorder.events), recorder.events) } if recorder.events[2].Type != EventTypeComplete { t.Fatalf("terminal = %q, want %q", recorder.events[2].Type, EventTypeComplete) } } type blockingSink struct { mu sync.Mutex events []RuntimeEvent firstEntered chan struct{} releaseFirst chan struct{} } func (s *blockingSink) Emit(_ context.Context, event RuntimeEvent) error { s.mu.Lock() first := len(s.events) == 0 s.events = append(s.events, event) s.mu.Unlock() if first { close(s.firstEntered) <-s.releaseFirst } return nil } func TestTerminalEmitterPreservesConcurrentAcceptedOrder(t *testing.T) { sink := &blockingSink{ firstEntered: make(chan struct{}), releaseFirst: make(chan struct{}), } emitter := NewTerminalEmitter(sink) deltaDone := make(chan error, 1) go func() { deltaDone <- emitter.Emit(context.Background(), RuntimeEvent{RunID: "run-1", Type: EventTypeDelta, Delta: "first"}) }() <-sink.firstEntered // Delta has entered sink.Emit and is blocked. completeDone := make(chan error, 1) go func() { completeDone <- emitter.Emit(context.Background(), RuntimeEvent{RunID: "run-1", Type: EventTypeComplete}) }() close(sink.releaseFirst) if err := <-deltaDone; err != nil { t.Fatalf("delta Emit failed: %v", err) } if err := <-completeDone; err != nil { t.Fatalf("complete Emit failed: %v", err) } sink.mu.Lock() defer sink.mu.Unlock() if len(sink.events) != 2 { t.Fatalf("events count = %d, want 2: %#v", len(sink.events), sink.events) } if sink.events[0].Type != EventTypeDelta || sink.events[1].Type != EventTypeComplete { t.Fatalf("events order = [%v, %v], want [EventTypeDelta, EventTypeComplete]", sink.events[0].Type, sink.events[1].Type) } }