fix(coderd/x/chatd): add chattest.OpenAI() default fake server (#24540)

- Add `chattest.OpenAI(t)` convenience wrapper around `NewOpenAI` with
sensible defaults (JSON title response for non-streaming, text chunk for
streaming)
- Update `seedChatDependencies` to use it instead of an empty base URL,
preventing title generation from hitting real `api.openai.com` with a
fake key:

```
    t.go:111: 2026-04-20 19:23:31.885 [debu]  coderd.chatd.processor: title model candidate failed  chat_id=edb43454-f23d-4163-9974-d101b8091de6  chat_id=edb43454-f23d-4163-9974-d101b8091de6 ...
        error= generate structured title:
                   github.com/coder/coder/v2/coderd/x/chatd.generateStructuredTitleWithUsage
                       /home/coder/src/coder/coder/coderd/x/chatd/quickgen.go:443
                 - unauthorized: Incorrect API key provided: test-api-key. You can find your API key at https://platform.openai.com/account/api-keys.
```

> 🤖
This commit is contained in:
Cian Johnston
2026-04-21 10:26:20 +01:00
committed by GitHub
parent bd3ed18fb1
commit 5f3effd839
2 changed files with 28 additions and 5 deletions
+8 -5
View File
@@ -1291,13 +1291,15 @@ func TestSendMessageInterruptBehaviorQueuesAndInterruptsWhenBusy(t *testing.T) {
require.NoError(t, err)
require.Len(t, queued, 1)
// Only the initial user message should be in chat_messages.
// Only messages from the initial processing round should be in
// chat_messages (user + assistant). The "interrupt" message must
// be in the queue, not inserted directly.
messages, err := db.GetChatMessagesByChatID(ctx, database.GetChatMessagesByChatIDParams{
ChatID: chat.ID,
AfterID: 0,
})
require.NoError(t, err)
require.Len(t, messages, 1)
require.Len(t, messages, 2)
}
func TestEditMessageUpdatesAndTruncatesAndClearsQueue(t *testing.T) {
@@ -1685,8 +1687,8 @@ func TestPromoteQueuedAllowsAlreadyQueuedMessageWhenUsageLimitReached(t *testing
AfterID: 0,
})
require.NoError(t, err)
require.Len(t, messages, 3)
require.Equal(t, database.ChatMessageRoleUser, messages[2].Role)
require.Len(t, messages, 4)
require.Equal(t, database.ChatMessageRoleUser, messages[3].Role)
}
func TestInterruptAutoPromotionIgnoresLaterUsageLimitIncrease(t *testing.T) {
@@ -4476,7 +4478,8 @@ func seedChatDependencies(
db database.Store,
) (database.User, database.Organization, database.ChatModelConfig) {
t.Helper()
return seedChatDependenciesWithProvider(ctx, t, db, "openai", "")
openAIURL := chattest.OpenAI(t)
return seedChatDependenciesWithProvider(ctx, t, db, "openai", openAIURL)
}
// seedChatDependenciesWithProvider creates a user, organization,
+20
View File
@@ -139,6 +139,26 @@ type openAIServer struct {
request *OpenAIRequest
}
// OpenAI creates a fake OpenAI-compatible test server with a
// sensible default handler and returns its base URL. It handles
// both the Responses API (/responses) and the Chat Completions
// API (/chat/completions).
//
// Non-streaming requests (e.g. structured-output title generation)
// receive a JSON payload satisfying the generatedTitle schema.
// Streaming requests (e.g. the main chat loop) receive a single
// text chunk. Use NewOpenAI when a test needs control over the
// response.
func OpenAI(t testing.TB) string {
t.Helper()
return NewOpenAI(t, func(req *OpenAIRequest) OpenAIResponse {
if req.Stream {
return OpenAIStreamingResponse(OpenAITextChunks("Hello from test server.")...)
}
return OpenAINonStreamingResponse(`{"title": "Test Chat"}`)
})
}
// NewOpenAI creates a new OpenAI test server with a handler function.
// The handler is called for each request and should return either a streaming
// response (via channel) or a non-streaming response.