TestPromoteQueuedWhileRequiresAction guards against the stops-dead failure mode: promoting on requires_action without closing pending dynamic tool calls leaves the assistant turn with unresolved tool_call parts that the LLM API rejects. It also asserts the synthetic tool-result row is published to l
(t *testing.T)
| 9150 | // also asserts the synthetic tool-result row is published to live |
| 9151 | // SSE subscribers before the promoted user message. |
| 9152 | func TestPromoteQueuedWhileRequiresAction(t *testing.T) { |
| 9153 | t.Parallel() |
| 9154 | |
| 9155 | db, ps := dbtestutil.NewDB(t) |
| 9156 | ctx := testutil.Context(t, testutil.WaitLong) |
| 9157 | |
| 9158 | var streamedCallCount atomic.Int32 |
| 9159 | openAIURL := chattest.NewOpenAI(t, func(req *chattest.OpenAIRequest) chattest.OpenAIResponse { |
| 9160 | if !req.Stream { |
| 9161 | return chattest.OpenAINonStreamingResponse("requires-action-promote") |
| 9162 | } |
| 9163 | if streamedCallCount.Add(1) == 1 { |
| 9164 | return chattest.OpenAIStreamingResponse( |
| 9165 | chattest.OpenAIToolCallChunk( |
| 9166 | "my_dynamic_tool", |
| 9167 | `{"input":"hello"}`, |
| 9168 | ), |
| 9169 | ) |
| 9170 | } |
| 9171 | // Second call: the resumed run after promote completes. |
| 9172 | return chattest.OpenAIStreamingResponse( |
| 9173 | chattest.OpenAITextChunks("Resumed after promotion.")..., |
| 9174 | ) |
| 9175 | }) |
| 9176 | |
| 9177 | user, org, model := seedChatDependenciesWithProvider(t, db, "openai-compat", openAIURL) |
| 9178 | server := newActiveTestServer(t, db, ps) |
| 9179 | |
| 9180 | dynamicToolsJSON, err := json.Marshal([]mcpgo.Tool{{ |
| 9181 | Name: "my_dynamic_tool", |
| 9182 | Description: "A test dynamic tool.", |
| 9183 | InputSchema: mcpgo.ToolInputSchema{ |
| 9184 | Type: "object", |
| 9185 | Properties: map[string]any{ |
| 9186 | "input": map[string]any{"type": "string"}, |
| 9187 | }, |
| 9188 | Required: []string{"input"}, |
| 9189 | }, |
| 9190 | }}) |
| 9191 | require.NoError(t, err) |
| 9192 | |
| 9193 | chat, err := server.CreateChat(ctx, chatd.CreateOptions{ |
| 9194 | OrganizationID: org.ID, |
| 9195 | OwnerID: user.ID, |
| 9196 | Title: "promote-while-requires-action", |
| 9197 | ModelConfigID: model.ID, |
| 9198 | InitialUserContent: []codersdk.ChatMessagePart{ |
| 9199 | codersdk.ChatMessageText("Please call the dynamic tool."), |
| 9200 | }, |
| 9201 | DynamicTools: dynamicToolsJSON, |
| 9202 | }) |
| 9203 | require.NoError(t, err) |
| 9204 | |
| 9205 | var chatBeforePromote database.Chat |
| 9206 | testutil.Eventually(ctx, t, func(ctx context.Context) bool { |
| 9207 | got, getErr := db.GetChatByID(ctx, chat.ID) |
| 9208 | if getErr != nil { |
| 9209 | return false |
nothing calls this directly
no test coverage detected