Skip to content

Commit 6778902

Browse files
test(2275): add 401-not-retried regression test (#178)
Locks in that HTTP 401 responses on /api/v1/audit/tool-call are terminal end-to-end through the WireMock HTTP path. Backstops getaxonflow/axonflow-enterprise#2275 where a customer's misconfigured deployment caused a tight 401 retry loop against community-saas (~30 401/hour from a single source IP). Java SDK is already safe: AxonFlow.handleErrorResponse maps 401 to AuthenticationException, and RetryExecutor.isRetryable returns false for AuthenticationException (src/main/java/com/getaxonflow/sdk/util/RetryExecutor.java:117-146). The existing RetryExecutorTest.shouldNotRetryOnAuthenticationException tests this at the exception-class level. This new test makes the contract explicit at the HTTP-status-code level for the specific endpoint named in #2275. Mutation-tested: removing AuthenticationException from the non-retryable list AND broadening the AxonFlowException retry check from `>= 500` to `>= 400` makes the test fail with `Expected exactly 1 requests matching the following pattern but received 3`, confirming the assertion isn't tautological. No code change; test-only. Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
1 parent 318b827 commit 6778902

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

src/test/java/com/getaxonflow/sdk/AuditToolCallTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,34 @@ void responseToStringShouldIncludeKeyFields() {
243243
assertThat(str).contains("aud_1");
244244
assertThat(str).contains("recorded");
245245
}
246+
247+
// Regression test for getaxonflow/axonflow-enterprise#2275: 401 on
248+
// /api/v1/audit/tool-call must be terminal — the SDK must NOT retry an
249+
// auth failure, because retrying with the same invalid token just
250+
// compounds the storm on the agent (716 × 401 / 24h observed from one
251+
// source IP against community-saas on 2026-05-19).
252+
//
253+
// Java SDK is already safe: the orchestrator response handler maps 401
254+
// to AuthenticationException (AxonFlow.java handleErrorResponse) and
255+
// RetryExecutor.isRetryable returns false for AuthenticationException
256+
// (RetryExecutor.java:119-122). This test locks in the contract
257+
// end-to-end through the WireMock HTTP path, since the existing
258+
// RetryExecutorTest.shouldNotRetryOnAuthenticationException is at the
259+
// exception-class level rather than the HTTP-status-code level.
260+
@Test
261+
@DisplayName("401 must not be retried — issue #2275")
262+
void shouldNotRetryOn401Issue2275() {
263+
stubFor(
264+
post(urlEqualTo("/api/v1/audit/tool-call"))
265+
.willReturn(aResponse().withStatus(401).withBody("{\"error\":\"unauthorized\"}")));
266+
267+
AuditToolCallRequest request =
268+
AuditToolCallRequest.builder().toolName("web_search").build();
269+
270+
assertThatThrownBy(() -> axonflow.auditToolCall(request))
271+
.isInstanceOf(AxonFlowException.class);
272+
273+
// wiremock counts requests; exactly one means the SDK did NOT retry.
274+
verify(1, postRequestedFor(urlEqualTo("/api/v1/audit/tool-call")));
275+
}
246276
}

0 commit comments

Comments
 (0)