Skip to content

Commit 7fc4861

Browse files
author
Mark Pollack
committed
Add test for handler exception to JSON-RPC error conversion
Validates that when typed handlers throw exceptions, the SDK properly converts them to JSON-RPC error responses with code -32603 (Internal Error). This confirms the correct error handling pattern for SDK users.
1 parent bb85a45 commit 7fc4861

File tree

1 file changed

+100
-0
lines changed

1 file changed

+100
-0
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2025-2025 the original author or authors.
3+
*/
4+
package com.agentclientprotocol.sdk.client;
5+
6+
import java.util.Map;
7+
import java.util.function.Function;
8+
9+
import com.agentclientprotocol.sdk.MockAcpClientTransport;
10+
import com.agentclientprotocol.sdk.spec.AcpSchema;
11+
import org.junit.jupiter.api.Test;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
/**
16+
* Tests that handler exceptions are properly converted to JSON-RPC error responses.
17+
* This validates the correct error handling pattern for SDK users.
18+
*/
19+
class HandlerExceptionTest {
20+
21+
/**
22+
* Verifies that when a typed handler throws an exception, the SDK converts it
23+
* to a proper JSON-RPC error response with code -32603 (Internal Error).
24+
*/
25+
@Test
26+
void handlerExceptionConvertedToJsonRpcError() throws InterruptedException {
27+
MockAcpClientTransport transport = new MockAcpClientTransport();
28+
String errorMessage = "File not found: /nonexistent.txt";
29+
30+
// Create a handler that throws an exception
31+
Function<AcpSchema.ReadTextFileRequest, AcpSchema.ReadTextFileResponse> handler = req -> {
32+
throw new RuntimeException(errorMessage);
33+
};
34+
35+
AcpSyncClient client = AcpClient.sync(transport)
36+
.readTextFileHandler(handler)
37+
.build();
38+
39+
// Simulate incoming request
40+
AcpSchema.JSONRPCRequest request = new AcpSchema.JSONRPCRequest(
41+
AcpSchema.JSONRPC_VERSION,
42+
"test-id",
43+
AcpSchema.METHOD_FS_READ_TEXT_FILE,
44+
Map.of("sessionId", "session-123", "path", "/nonexistent.txt")
45+
);
46+
transport.simulateIncomingMessage(request);
47+
48+
// Wait for async processing
49+
Thread.sleep(500);
50+
51+
// Verify it's a JSON-RPC error response
52+
assertThat(transport.getSentMessages()).hasSize(1);
53+
assertThat(transport.getSentMessages().get(0)).isInstanceOf(AcpSchema.JSONRPCResponse.class);
54+
AcpSchema.JSONRPCResponse response = (AcpSchema.JSONRPCResponse) transport.getSentMessages().get(0);
55+
56+
assertThat(response.id()).isEqualTo("test-id");
57+
assertThat(response.result()).isNull();
58+
assertThat(response.error()).isNotNull();
59+
assertThat(response.error().code()).isEqualTo(-32603); // Internal Error
60+
assertThat(response.error().message()).contains(errorMessage);
61+
62+
client.close();
63+
}
64+
65+
/**
66+
* Verifies that IOException from file operations is also converted to JSON-RPC error.
67+
*/
68+
@Test
69+
void ioExceptionConvertedToJsonRpcError() throws InterruptedException {
70+
MockAcpClientTransport transport = new MockAcpClientTransport();
71+
72+
// Create a handler that throws IOException (wrapped in RuntimeException per Java patterns)
73+
Function<AcpSchema.WriteTextFileRequest, AcpSchema.WriteTextFileResponse> handler = req -> {
74+
throw new RuntimeException(new java.io.IOException("Permission denied: " + req.path()));
75+
};
76+
77+
AcpSyncClient client = AcpClient.sync(transport)
78+
.writeTextFileHandler(handler)
79+
.build();
80+
81+
AcpSchema.JSONRPCRequest request = new AcpSchema.JSONRPCRequest(
82+
AcpSchema.JSONRPC_VERSION,
83+
"test-id-2",
84+
AcpSchema.METHOD_FS_WRITE_TEXT_FILE,
85+
Map.of("sessionId", "session-123", "path", "/readonly/file.txt", "content", "test")
86+
);
87+
transport.simulateIncomingMessage(request);
88+
89+
// Wait for async processing
90+
Thread.sleep(500);
91+
92+
assertThat(transport.getSentMessages()).hasSize(1);
93+
AcpSchema.JSONRPCResponse response = (AcpSchema.JSONRPCResponse) transport.getSentMessages().get(0);
94+
assertThat(response.error()).isNotNull();
95+
assertThat(response.error().code()).isEqualTo(-32603);
96+
assertThat(response.error().message()).contains("Permission denied");
97+
98+
client.close();
99+
}
100+
}

0 commit comments

Comments
 (0)