diff --git a/internal/chat/sse_test.go b/internal/chat/sse_test.go index 928d29e..f1a8398 100644 --- a/internal/chat/sse_test.go +++ b/internal/chat/sse_test.go @@ -183,8 +183,13 @@ func TestStreamMessages_StopsWhenClientClosesContext(t *testing.T) { r.ServeHTTP(w, req) }() - // Send one event, then cancel. + // Send one event and give the handler a moment to drain it before cancelling, + // so the body assertion is deterministic. The cancel itself is what we're + // verifying — that the handler exits even though the channel never closes. ch <- SSEEvent{ID: 1, Event: "text_delta", Data: "hi"} + require.Eventually(t, func() bool { + return strings.Contains(w.Body.String(), "text_delta") + }, time.Second, 10*time.Millisecond, "event was not flushed before cancel") cancel() // Handler must return promptly after context cancellation. @@ -195,7 +200,6 @@ func TestStreamMessages_StopsWhenClientClosesContext(t *testing.T) { t.Fatal("handler did not stop after context cancellation") } - // The one event before cancel should have been written. assert.Contains(t, w.Body.String(), "text_delta") } diff --git a/internal/chat/streamer.go b/internal/chat/streamer.go index 6d76f39..2d7a1b4 100644 --- a/internal/chat/streamer.go +++ b/internal/chat/streamer.go @@ -107,23 +107,32 @@ type streamerHub struct { } // NewStreamerHub constructs a StreamerHub. +// convSvc must be the concrete *conversationService produced by NewConversationService; +// the type assertion lets callers in other packages pass the interface without needing +// to import the unexported concrete type. Pass nil to skip title generation. func NewStreamerHub( repo Repository, registry clientGetter, dispatch notifyDispatcher, auditor audit.Recorder, - convSvc *conversationService, + convSvc ConversationService, log *slog.Logger, retention time.Duration, safetyPct float64, ) StreamerHub { + var concrete *conversationService + if convSvc != nil { + if c, ok := convSvc.(*conversationService); ok { + concrete = c + } + } return &streamerHub{ byMsgID: make(map[int64]*streamer), repo: repo, registry: registry, dispatch: dispatch, auditor: auditor, - convSvc: convSvc, + convSvc: concrete, log: log, retention: retention, safety: safetyPct,