Files
coder/coderd/x/chatd/export_test.go
T
Ethan 4e08543ace test(coderd): centralize chat test harness and stabilize flakes (#25171)
Chat tests previously constructed a real `openai` provider with a fake
API key and no `BaseURL`, so background title generation hit
`api.openai.com` and timed out under `-race`. The same root cause
produced several distinct flakes: title regeneration races with
synchronous `UpdateChat`/`ProposeChatTitle`, and pagination races
against `updated_at` bumps from real-network processing.

This moves the fake OpenAI-compatible provider and the chat-settle wait
into first-class `coderdtest` capabilities.
`coderd.Options.ChatProviderAPIKeys` is the new seam tests use to
redirect chat traffic to a local `httptest.Server`.
`coderdtest.WaitForChatSettled` replaces per-test waiters and drains
tracked chat-daemon work after the chat row leaves `pending`/`running`.
The `newChatClient*` constructors funnel through one options builder
that installs the fake provider before the coderd test server so cleanup
ordering is deterministic.

Closes https://github.com/coder/internal/issues/1528 & Closes ENG-2659
Closes https://github.com/coder/internal/issues/1480 & Closes CODAGT-359
Closes https://github.com/coder/internal/issues/1507 & Closes CODAGT-368
Relates to https://github.com/coder/internal/issues/1397 & Relates to
CODAGT-374
2026-05-12 22:13:55 +10:00

63 lines
1.9 KiB
Go

package chatd
import (
"context"
"github.com/sqlc-dev/pqtype"
"cdr.dev/slog/v3"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/codersdk"
)
// FinishActiveChatForTest exposes the unexported cleanup TX so tests
// can drive the post-run state machine deterministically. Returns the
// resulting chat, the promoted message (if any), the synthetic
// tool-result rows the cleanup TX inserted (if any), and the cleanup
// error. The lastError string is encoded into a structured payload
// the same way runChat does, so callers do not need to know about
// the structured-error wrapper.
func FinishActiveChatForTest(
ctx context.Context,
server *Server,
chat database.Chat,
status database.ChatStatus,
lastError string,
) (database.Chat, *database.ChatMessage, []database.ChatMessage, error) {
logger := server.logger.With(slog.F("chat_id", chat.ID))
var encoded pqtype.NullRawMessage
if lastError != "" {
var err error
encoded, err = encodeChatLastErrorPayload(&codersdk.ChatError{
Message: lastError,
})
if err != nil {
return database.Chat{}, nil, nil, err
}
}
result, err := server.finishActiveChat(ctx, logger, chat, status, encoded)
if err != nil {
return database.Chat{}, nil, nil, err
}
return result.updatedChat, result.promotedMessage, result.syntheticToolResults, nil
}
// RecoverStaleChatsForTest exposes the unexported stale-recovery loop
// so tests can assert the recovery state machine without waiting for
// the periodic ticker.
func RecoverStaleChatsForTest(ctx context.Context, server *Server) {
server.recoverStaleChats(ctx)
}
// InsertSyntheticToolResultsTxForTest exposes the unexported helper
// so tests can verify the dedup path against pre-existing tool
// results.
func InsertSyntheticToolResultsTxForTest(
ctx context.Context,
store database.Store,
chat database.Chat,
reason string,
) ([]database.ChatMessage, error) {
return insertSyntheticToolResultsTx(ctx, store, chat, reason)
}