10
10
import java .util .function .Function ;
11
11
12
12
import io .modelcontextprotocol .spec .ClientMcpTransport ;
13
+ import io .modelcontextprotocol .spec .McpError ;
13
14
import io .modelcontextprotocol .spec .McpSchema ;
14
15
import io .modelcontextprotocol .spec .McpSchema .CallToolRequest ;
15
16
import io .modelcontextprotocol .spec .McpSchema .ClientCapabilities ;
@@ -47,8 +48,6 @@ public abstract class AbstractMcpAsyncClientTests {
47
48
48
49
protected ClientMcpTransport mcpTransport ;
49
50
50
- private static final Duration TIMEOUT = Duration .ofSeconds (20 );
51
-
52
51
private static final String ECHO_TEST_MESSAGE = "Hello MCP Spring AI!" ;
53
52
54
53
abstract protected ClientMcpTransport createMcpTransport ();
@@ -59,17 +58,20 @@ protected void onStart() {
59
58
protected void onClose () {
60
59
}
61
60
61
+ protected Duration getTimeoutDuration () {
62
+ return Duration .ofSeconds (2 );
63
+ }
64
+
62
65
@ BeforeEach
63
66
void setUp () {
64
67
onStart ();
65
68
this .mcpTransport = createMcpTransport ();
66
69
67
70
assertThatCode (() -> {
68
71
mcpAsyncClient = McpClient .async (mcpTransport )
69
- .requestTimeout (TIMEOUT )
72
+ .requestTimeout (getTimeoutDuration () )
70
73
.capabilities (ClientCapabilities .builder ().roots (true ).build ())
71
74
.build ();
72
- mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
73
75
}).doesNotThrowAnyException ();
74
76
}
75
77
@@ -92,8 +94,16 @@ void testConstructorWithInvalidArguments() {
92
94
.hasMessage ("Request timeout must not be null" );
93
95
}
94
96
97
+ @ Test
98
+ void testListToolsWithoutInitialization () {
99
+ assertThatThrownBy (() -> mcpAsyncClient .listTools (null ).block ()).isInstanceOf (McpError .class )
100
+ .hasMessage ("Client must be initialized before listing tools" );
101
+ }
102
+
95
103
@ Test
96
104
void testListTools () {
105
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
106
+
97
107
StepVerifier .create (mcpAsyncClient .listTools (null )).consumeNextWith (result -> {
98
108
assertThat (result .tools ()).isNotNull ().isNotEmpty ();
99
109
@@ -103,13 +113,30 @@ void testListTools() {
103
113
}).verifyComplete ();
104
114
}
105
115
116
+ @ Test
117
+ void testPingWithoutInitialization () {
118
+ assertThatThrownBy (() -> mcpAsyncClient .ping ().block ()).isInstanceOf (McpError .class )
119
+ .hasMessage ("Client must be initialized before pinging the server" );
120
+ }
121
+
106
122
@ Test
107
123
void testPing () {
124
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
108
125
assertThatCode (() -> mcpAsyncClient .ping ().block ()).doesNotThrowAnyException ();
109
126
}
110
127
128
+ @ Test
129
+ void testCallToolWithoutInitialization () {
130
+ CallToolRequest callToolRequest = new CallToolRequest ("echo" , Map .of ("message" , ECHO_TEST_MESSAGE ));
131
+
132
+ assertThatThrownBy (() -> mcpAsyncClient .callTool (callToolRequest ).block ()).isInstanceOf (McpError .class )
133
+ .hasMessage ("Client must be initialized before calling tools" );
134
+ }
135
+
111
136
@ Test
112
137
void testCallTool () {
138
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
139
+
113
140
CallToolRequest callToolRequest = new CallToolRequest ("echo" , Map .of ("message" , ECHO_TEST_MESSAGE ));
114
141
115
142
StepVerifier .create (mcpAsyncClient .callTool (callToolRequest )).consumeNextWith (callToolResult -> {
@@ -122,13 +149,23 @@ void testCallTool() {
122
149
123
150
@ Test
124
151
void testCallToolWithInvalidTool () {
152
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
153
+
125
154
CallToolRequest invalidRequest = new CallToolRequest ("nonexistent_tool" , Map .of ("message" , ECHO_TEST_MESSAGE ));
126
155
127
156
assertThatThrownBy (() -> mcpAsyncClient .callTool (invalidRequest ).block ()).isInstanceOf (Exception .class );
128
157
}
129
158
159
+ @ Test
160
+ void testListResourcesWithoutInitialization () {
161
+ assertThatThrownBy (() -> mcpAsyncClient .listResources (null ).block ()).isInstanceOf (McpError .class )
162
+ .hasMessage ("Client must be initialized before listing resources" );
163
+ }
164
+
130
165
@ Test
131
166
void testListResources () {
167
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
168
+
132
169
StepVerifier .create (mcpAsyncClient .listResources (null )).consumeNextWith (resources -> {
133
170
assertThat (resources ).isNotNull ().satisfies (result -> {
134
171
assertThat (result .resources ()).isNotNull ();
@@ -147,8 +184,16 @@ void testMcpAsyncClientState() {
147
184
assertThat (mcpAsyncClient ).isNotNull ();
148
185
}
149
186
187
+ @ Test
188
+ void testListPromptsWithoutInitialization () {
189
+ assertThatThrownBy (() -> mcpAsyncClient .listPrompts (null ).block ()).isInstanceOf (McpError .class )
190
+ .hasMessage ("Client must be initialized before listing prompts" );
191
+ }
192
+
150
193
@ Test
151
194
void testListPrompts () {
195
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
196
+
152
197
StepVerifier .create (mcpAsyncClient .listPrompts (null )).consumeNextWith (prompts -> {
153
198
assertThat (prompts ).isNotNull ().satisfies (result -> {
154
199
assertThat (result .prompts ()).isNotNull ();
@@ -162,8 +207,18 @@ void testListPrompts() {
162
207
}).verifyComplete ();
163
208
}
164
209
210
+ @ Test
211
+ void testGetPromptWithoutInitialization () {
212
+ GetPromptRequest request = new GetPromptRequest ("simple_prompt" , Map .of ());
213
+
214
+ assertThatThrownBy (() -> mcpAsyncClient .getPrompt (request ).block ()).isInstanceOf (McpError .class )
215
+ .hasMessage ("Client must be initialized before getting prompts" );
216
+ }
217
+
165
218
@ Test
166
219
void testGetPrompt () {
220
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
221
+
167
222
StepVerifier .create (mcpAsyncClient .getPrompt (new GetPromptRequest ("simple_prompt" , Map .of ())))
168
223
.consumeNextWith (prompt -> {
169
224
assertThat (prompt ).isNotNull ().satisfies (result -> {
@@ -174,8 +229,16 @@ void testGetPrompt() {
174
229
.verifyComplete ();
175
230
}
176
231
232
+ @ Test
233
+ void testRootsListChangedWithoutInitialization () {
234
+ assertThatThrownBy (() -> mcpAsyncClient .rootsListChangedNotification ().block ()).isInstanceOf (McpError .class )
235
+ .hasMessage ("Client must be initialized before sending roots list changed notification" );
236
+ }
237
+
177
238
@ Test
178
239
void testRootsListChanged () {
240
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
241
+
179
242
assertThatCode (() -> mcpAsyncClient .rootsListChangedNotification ().block ()).doesNotThrowAnyException ();
180
243
}
181
244
@@ -184,7 +247,7 @@ void testInitializeWithRootsListProviders() {
184
247
var transport = createMcpTransport ();
185
248
186
249
var client = McpClient .async (transport )
187
- .requestTimeout (TIMEOUT )
250
+ .requestTimeout (getTimeoutDuration () )
188
251
.roots (new Root ("file:///test/path" , "test-root" ))
189
252
.build ();
190
253
@@ -233,8 +296,16 @@ void testReadResource() {
233
296
}).verifyComplete ();
234
297
}
235
298
299
+ @ Test
300
+ void testListResourceTemplatesWithoutInitialization () {
301
+ assertThatThrownBy (() -> mcpAsyncClient .listResourceTemplates ().block ()).isInstanceOf (McpError .class )
302
+ .hasMessage ("Client must be initialized before listing resource templates" );
303
+ }
304
+
236
305
@ Test
237
306
void testListResourceTemplates () {
307
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
308
+
238
309
StepVerifier .create (mcpAsyncClient .listResourceTemplates ()).consumeNextWith (result -> {
239
310
assertThat (result ).isNotNull ();
240
311
assertThat (result .resourceTemplates ()).isNotNull ();
@@ -266,7 +337,7 @@ void testNotificationHandlers() {
266
337
267
338
var transport = createMcpTransport ();
268
339
var client = McpClient .async (transport )
269
- .requestTimeout (TIMEOUT )
340
+ .requestTimeout (getTimeoutDuration () )
270
341
.toolsChangeConsumer (tools -> Mono .fromRunnable (() -> toolsNotificationReceived .set (true )))
271
342
.resourcesChangeConsumer (resources -> Mono .fromRunnable (() -> resourcesNotificationReceived .set (true )))
272
343
.promptsChangeConsumer (prompts -> Mono .fromRunnable (() -> promptsNotificationReceived .set (true )))
@@ -285,7 +356,7 @@ void testInitializeWithSamplingCapability() {
285
356
var capabilities = ClientCapabilities .builder ().sampling ().build ();
286
357
287
358
var client = McpClient .async (transport )
288
- .requestTimeout (TIMEOUT )
359
+ .requestTimeout (getTimeoutDuration () )
289
360
.capabilities (capabilities )
290
361
.sampling (request -> Mono .just (CreateMessageResult .builder ().message ("test" ).model ("test-model" ).build ()))
291
362
.build ();
@@ -309,7 +380,7 @@ void testInitializeWithAllCapabilities() {
309
380
Function <CreateMessageRequest , Mono <CreateMessageResult >> samplingHandler = request -> Mono
310
381
.just (CreateMessageResult .builder ().message ("test" ).model ("test-model" ).build ());
311
382
var client = McpClient .async (transport )
312
- .requestTimeout (TIMEOUT )
383
+ .requestTimeout (getTimeoutDuration () )
313
384
.capabilities (capabilities )
314
385
.sampling (samplingHandler )
315
386
.build ();
@@ -326,8 +397,17 @@ void testInitializeWithAllCapabilities() {
326
397
// Logging Tests
327
398
// ---------------------------------------
328
399
400
+ @ Test
401
+ void testLoggingLevelsWithoutInitialization () {
402
+ assertThatThrownBy (() -> mcpAsyncClient .setLoggingLevel (McpSchema .LoggingLevel .DEBUG ).block ())
403
+ .isInstanceOf (McpError .class )
404
+ .hasMessage ("Client must be initialized before setting logging level" );
405
+ }
406
+
329
407
@ Test
330
408
void testLoggingLevels () {
409
+ mcpAsyncClient .initialize ().block (Duration .ofSeconds (10 ));
410
+
331
411
// Test all logging levels
332
412
for (McpSchema .LoggingLevel level : McpSchema .LoggingLevel .values ()) {
333
413
StepVerifier .create (mcpAsyncClient .setLoggingLevel (level )).verifyComplete ();
@@ -340,7 +420,7 @@ void testLoggingConsumer() {
340
420
var transport = createMcpTransport ();
341
421
342
422
var client = McpClient .async (transport )
343
- .requestTimeout (TIMEOUT )
423
+ .requestTimeout (getTimeoutDuration () )
344
424
.loggingConsumer (notification -> Mono .fromRunnable (() -> logReceived .set (true )))
345
425
.build ();
346
426
0 commit comments