Skip to content

Commit 3bdc366

Browse files
committed
Stop HandlerExceptionTest racing the async dispatch
Both tests slept a fixed 500ms and then asserted the response had been sent. The handler runs asynchronously, so on a loaded machine the sleep expires first: the test failed 4 of 5 local runs at the previous commit and twice in a row on CI. Poll for the expected message to a 10s deadline instead. 10 consecutive local runs pass, and the happy path is faster than the old fixed sleep.
1 parent 75ebbe7 commit 3bdc366

1 file changed

Lines changed: 23 additions & 6 deletions

File tree

acp-core/src/test/java/com/agentclientprotocol/sdk/client/HandlerExceptionTest.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44
package com.agentclientprotocol.sdk.client;
55

6+
import java.time.Duration;
67
import java.util.Map;
78
import java.util.function.Function;
89

@@ -23,7 +24,7 @@ class HandlerExceptionTest {
2324
* to a proper JSON-RPC error response with code -32603 (Internal Error).
2425
*/
2526
@Test
26-
void handlerExceptionConvertedToJsonRpcError() throws InterruptedException {
27+
void handlerExceptionConvertedToJsonRpcError() {
2728
MockAcpClientTransport transport = new MockAcpClientTransport();
2829
String errorMessage = "File not found: /nonexistent.txt";
2930

@@ -45,8 +46,7 @@ void handlerExceptionConvertedToJsonRpcError() throws InterruptedException {
4546
);
4647
transport.simulateIncomingMessage(request);
4748

48-
// Wait for async processing
49-
Thread.sleep(500);
49+
awaitSentMessages(transport, 1);
5050

5151
// Verify it's a JSON-RPC error response
5252
assertThat(transport.getSentMessages()).hasSize(1);
@@ -66,7 +66,7 @@ void handlerExceptionConvertedToJsonRpcError() throws InterruptedException {
6666
* Verifies that IOException from file operations is also converted to JSON-RPC error.
6767
*/
6868
@Test
69-
void ioExceptionConvertedToJsonRpcError() throws InterruptedException {
69+
void ioExceptionConvertedToJsonRpcError() {
7070
MockAcpClientTransport transport = new MockAcpClientTransport();
7171

7272
// Create a handler that throws IOException (wrapped in RuntimeException per Java patterns)
@@ -86,8 +86,7 @@ void ioExceptionConvertedToJsonRpcError() throws InterruptedException {
8686
);
8787
transport.simulateIncomingMessage(request);
8888

89-
// Wait for async processing
90-
Thread.sleep(500);
89+
awaitSentMessages(transport, 1);
9190

9291
assertThat(transport.getSentMessages()).hasSize(1);
9392
AcpSchema.JSONRPCResponse response = (AcpSchema.JSONRPCResponse) transport.getSentMessages().get(0);
@@ -97,4 +96,22 @@ void ioExceptionConvertedToJsonRpcError() throws InterruptedException {
9796

9897
client.close();
9998
}
99+
100+
/**
101+
* Waits for the transport to have sent at least {@code expected} messages. The
102+
* handler runs asynchronously, so a fixed sleep races the dispatch and fails
103+
* intermittently on a loaded machine; poll to a generous deadline instead.
104+
*/
105+
private static void awaitSentMessages(MockAcpClientTransport transport, int expected) {
106+
long deadline = System.nanoTime() + Duration.ofSeconds(10).toNanos();
107+
while (transport.getSentMessages().size() < expected && System.nanoTime() < deadline) {
108+
try {
109+
Thread.sleep(10);
110+
}
111+
catch (InterruptedException ex) {
112+
Thread.currentThread().interrupt();
113+
return;
114+
}
115+
}
116+
}
100117
}

0 commit comments

Comments
 (0)