diff --git a/src/app/v1/_lib/proxy/forwarder.ts b/src/app/v1/_lib/proxy/forwarder.ts index 47002d6dc..10dc55885 100644 --- a/src/app/v1/_lib/proxy/forwarder.ts +++ b/src/app/v1/_lib/proxy/forwarder.ts @@ -135,6 +135,10 @@ function decodeRequestBodyAsJson(body: BodyInit | undefined): Record { }); }); +describe("ProxyForwarder - client request upstream errors", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(categorizeErrorAsync).mockResolvedValue(ErrorCategory.PROVIDER_ERROR); + }); + + test("upstream 400 should not retry or count against provider circuit breaker", async () => { + const session = createSession(); + const provider = createProvider({ + providerVendorId: null, + maxRetryAttempts: 4, + }); + session.setProvider(provider); + + const doForward = vi.spyOn( + ProxyForwarder as unknown as { doForward: (...args: unknown[]) => unknown }, + "doForward" + ); + doForward.mockImplementationOnce(async () => { + throw new ProxyError("Provider returned 400: Bad Request", 400, { + body: '{"detail":"Bad Request"}', + providerId: provider.id, + providerName: provider.name, + }); + }); + + await expect(ProxyForwarder.send(session)).rejects.toMatchObject({ + statusCode: 400, + }); + + expect(doForward).toHaveBeenCalledTimes(1); + expect(mocks.recordFailure).not.toHaveBeenCalled(); + }); +}); + describe("ProxyForwarder - endpoint stickiness on retry", () => { beforeEach(() => { vi.clearAllMocks();