From 9f99a7bc0b5482ec1e1b70376d44c3df963ffec2 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 15 May 2026 09:33:54 -0400 Subject: [PATCH] fix(coderd): stabilize TestPatchChatMessage/ChangesModel flaky test (#25306) Fixes coder/internal#1535 ## Problem `TestPatchChatMessage/ChangesModel` is flaky because it races with the chat daemon's background processing. `CreateChat` sets the chat to `pending` and the daemon picks it up asynchronously. The test immediately calls `EditChatMessage` (which changes the model to an override) while the first processing round is still running. The `InsertChatMessages` SQL CTE unconditionally updates `chats.last_model_config_id` to the model of the last inserted message. When the daemon's in-flight message insertions commit after the edit transaction, they overwrite `last_model_config_id` back to the default model. Similarly, after the edit sets the chat back to `pending`, the daemon re-processes it. The test's `GetChat` call could race with this second round. ## Fix Poll for the chat to reach `waiting` (or `error`) status: 1. **Before editing**: wait for the initial processing round to complete 2. **After editing**: wait for the second processing round (triggered by the edit) to complete Then assert `last_model_config_id`, which is now stable. > Generated with [Coder Agents](https://coder.com/agents) by @kylecarbs --- coderd/exp_chats_test.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/coderd/exp_chats_test.go b/coderd/exp_chats_test.go index 61720a426c..77ad68acb6 100644 --- a/coderd/exp_chats_test.go +++ b/coderd/exp_chats_test.go @@ -7792,6 +7792,20 @@ func TestPatchChatMessage(t *testing.T) { require.Equal(t, defaultModel.ID, chat.LastModelConfigID, "chat starts on the default model") + // Wait for the initial chat processing to complete before + // editing. CreateChat sets the chat to pending and the daemon + // processes it asynchronously; editing while that first round + // is still running can race with message insertions that + // overwrite last_model_config_id. + testutil.Eventually(ctx, t, func(ctx context.Context) bool { + c, getErr := client.GetChat(ctx, chat.ID) + if getErr != nil { + return false + } + return c.Status != codersdk.ChatStatusPending && + c.Status != codersdk.ChatStatusRunning + }, testutil.IntervalFast, "initial chat processing did not finish") + messagesResult, err := client.GetChatMessages(ctx, chat.ID, nil) require.NoError(t, err) var userMessageID int64 @@ -7816,6 +7830,19 @@ func TestPatchChatMessage(t *testing.T) { require.Equal(t, overrideModel.ID, *edited.Message.ModelConfigID, "replacement message must use the requested model") + // Wait for the second round of processing (triggered by the + // edit) to complete, then verify last_model_config_id. + // Reading immediately after EditChatMessage can race with the + // daemon re-processing the now-pending chat. + testutil.Eventually(ctx, t, func(ctx context.Context) bool { + c, getErr := client.GetChat(ctx, chat.ID) + if getErr != nil { + return false + } + return c.Status != codersdk.ChatStatusPending && + c.Status != codersdk.ChatStatusRunning + }, testutil.IntervalFast, "post-edit chat processing did not finish") + updatedChat, err := client.GetChat(ctx, chat.ID) require.NoError(t, err) require.Equal(t, overrideModel.ID, updatedChat.LastModelConfigID,