mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user