diff --git a/tests/adapters/adapter-inner-send-budget-wiring.test.ts b/tests/adapters/adapter-inner-send-budget-wiring.test.ts index 9885c73af9..c5341a759f 100644 --- a/tests/adapters/adapter-inner-send-budget-wiring.test.ts +++ b/tests/adapters/adapter-inner-send-budget-wiring.test.ts @@ -13,6 +13,7 @@ import { createDevinAdapter, DEVIN_API_SERVER } from "../../src/adapters/devin"; import { setCachedCatalogForTests } from "../../src/adapters/devin/cloud-direct/catalog"; import { resetKiroThrottleStateForTests } from "../../src/adapters/kiro-retry"; import type { AdapterFetchContext } from "../../src/adapters/base"; +import { SendBudgetExhaustedError } from "../../src/lib/upstream-retry"; import { encodeMessage } from "../../src/lib/eventstream-decoder"; import { createRequestExecutionBudget, type RequestExecutionBudgetPolicy } from "../../src/lib/request-execution-budget"; import type { AdapterEvent, OcxParsedRequest, OcxProviderConfig } from "../../src/types"; @@ -206,6 +207,7 @@ describe("Devin runTurn execution wiring", () => { adapter: "devin", baseUrl: DEVIN_API_SERVER, apiKey, } as unknown as OcxProviderConfig); + let refusal: unknown; try { await adapter.runTurn?.( { @@ -223,7 +225,7 @@ describe("Devin runTurn execution wiring", () => { onPhysicalSend: send => { observed.push(send); }, }, event => events.push(event), - ).catch(() => { /* the refusal escapes the adapter for the caller to map */ }); + ).catch((error: unknown) => { refusal = error; }); } finally { setCachedCatalogForTests(null); if (previousHome === undefined) delete process.env.OPENCODEX_HOME; @@ -233,6 +235,11 @@ describe("Devin runTurn execution wiring", () => { removeTreeWithRetry(home); } + // The refusal escapes the adapter for the caller to map, and WHICH error escapes is the + // contract: a bare catch here passed even when the adapter threw something else entirely, + // which would have left the turn reported as an ordinary upstream failure instead of a + // budget refusal. + expect(refusal).toBeInstanceOf(SendBudgetExhaustedError); expect(urls.filter(url => url.includes("GetChatMessage"))).toHaveLength(0); expect(observed).toEqual([]); expect(budget.used).toBe(0); diff --git a/tests/responses/responses-send-budget-counts.test.ts b/tests/responses/responses-send-budget-counts.test.ts index d6119caa0f..7585db3728 100644 --- a/tests/responses/responses-send-budget-counts.test.ts +++ b/tests/responses/responses-send-budget-counts.test.ts @@ -217,12 +217,17 @@ describe("upstream sends per logical request", () => { // without proving the refusal was recorded against the turn that was refused. expect(attempts).toHaveLength(1); expect({ + // Pinned, not merely reported in the failure message. The refusal reaches the client as + // an error code on a buffered FAILED response, so the outer HTTP status stays 200; a + // reader who assumes 429 here would be describing a transport this path never uses. + status: response.status, adapter: attempts[0]?.adapter, sendCount: attempts[0]?.sendCount, chatCalls: urls.filter(url => url.includes("GetChatMessage")).length, totalSends: totalSends(logCtx), refused: body.includes("request_send_budget_exhausted"), }, `status ${response.status}: ${body.slice(0, 240)}`).toEqual({ + status: 200, adapter: "devin", sendCount: 0, chatCalls: 0,