Skip to content

Commit b382ac5

Browse files
committed
refactor: improve MCP server test architecture with parameterized tests
Signed-off-by: Christian Tzolov <[email protected]>
1 parent 6e66602 commit b382ac5

File tree

6 files changed

+87
-121
lines changed

6 files changed

+87
-121
lines changed

mcp/src/test/java/io/modelcontextprotocol/server/AbstractMcpAsyncServerTests.java

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.junit.jupiter.api.AfterEach;
2222
import org.junit.jupiter.api.BeforeEach;
2323
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.params.ParameterizedTest;
25+
import org.junit.jupiter.params.provider.ValueSource;
2426
import reactor.core.publisher.Mono;
2527
import reactor.test.StepVerifier;
2628

@@ -43,7 +45,7 @@ public abstract class AbstractMcpAsyncServerTests {
4345

4446
private static final String TEST_PROMPT_NAME = "test-prompt";
4547

46-
abstract protected McpServerTransportProvider createMcpTransportProvider();
48+
abstract protected McpServer.AsyncSpecification<?> prepareAsyncServerBuilder();
4749

4850
protected void onStart() {
4951
}
@@ -64,28 +66,29 @@ void tearDown() {
6466
// Server Lifecycle Tests
6567
// ---------------------------------------
6668

67-
@Test
68-
void testConstructorWithInvalidArguments() {
69+
@ParameterizedTest(name = "{0} : {displayName} ")
70+
@ValueSource(strings = { "sse", "streamable" })
71+
void testConstructorWithInvalidArguments(String serverType) {
6972
assertThatThrownBy(() -> McpServer.async((McpServerTransportProvider) null))
7073
.isInstanceOf(IllegalArgumentException.class)
7174
.hasMessage("Transport provider must not be null");
7275

73-
assertThatThrownBy(
74-
() -> McpServer.async(createMcpTransportProvider()).serverInfo((McpSchema.Implementation) null))
76+
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo((McpSchema.Implementation) null))
7577
.isInstanceOf(IllegalArgumentException.class)
7678
.hasMessage("Server info must not be null");
7779
}
7880

7981
@Test
8082
void testGracefulShutdown() {
81-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider()).serverInfo("test-server", "1.0.0").build();
83+
McpServer.AsyncSpecification<?> builder = prepareAsyncServerBuilder();
84+
var mcpAsyncServer = builder.serverInfo("test-server", "1.0.0").build();
8285

8386
StepVerifier.create(mcpAsyncServer.closeGracefully()).verifyComplete();
8487
}
8588

8689
@Test
8790
void testImmediateClose() {
88-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider()).serverInfo("test-server", "1.0.0").build();
91+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
8992

9093
assertThatCode(() -> mcpAsyncServer.close()).doesNotThrowAnyException();
9194
}
@@ -105,8 +108,7 @@ void testImmediateClose() {
105108
@Deprecated
106109
void testAddTool() {
107110
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
108-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
109-
.serverInfo("test-server", "1.0.0")
111+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
110112
.capabilities(ServerCapabilities.builder().tools(true).build())
111113
.build();
112114

@@ -120,8 +122,7 @@ void testAddTool() {
120122
@Test
121123
void testAddToolCall() {
122124
Tool newTool = new McpSchema.Tool("new-tool", "New test tool", emptyJsonSchema);
123-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
124-
.serverInfo("test-server", "1.0.0")
125+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
125126
.capabilities(ServerCapabilities.builder().tools(true).build())
126127
.build();
127128

@@ -138,8 +139,7 @@ void testAddToolCall() {
138139
void testAddDuplicateTool() {
139140
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
140141

141-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
142-
.serverInfo("test-server", "1.0.0")
142+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
143143
.capabilities(ServerCapabilities.builder().tools(true).build())
144144
.tool(duplicateTool, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))
145145
.build();
@@ -159,8 +159,7 @@ void testAddDuplicateTool() {
159159
void testAddDuplicateToolCall() {
160160
Tool duplicateTool = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
161161

162-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
163-
.serverInfo("test-server", "1.0.0")
162+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
164163
.capabilities(ServerCapabilities.builder().tools(true).build())
165164
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
166165
.build();
@@ -181,8 +180,7 @@ void testDuplicateToolCallDuringBuilding() {
181180
Tool duplicateTool = new Tool("duplicate-build-toolcall", "Duplicate toolcall during building",
182181
emptyJsonSchema);
183182

184-
assertThatThrownBy(() -> McpServer.async(createMcpTransportProvider())
185-
.serverInfo("test-server", "1.0.0")
183+
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
186184
.capabilities(ServerCapabilities.builder().tools(true).build())
187185
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
188186
.toolCall(duplicateTool, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false))) // Duplicate!
@@ -204,8 +202,7 @@ void testDuplicateToolsInBatchListRegistration() {
204202
.build() // Duplicate!
205203
);
206204

207-
assertThatThrownBy(() -> McpServer.async(createMcpTransportProvider())
208-
.serverInfo("test-server", "1.0.0")
205+
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
209206
.capabilities(ServerCapabilities.builder().tools(true).build())
210207
.tools(specs)
211208
.build()).isInstanceOf(IllegalArgumentException.class)
@@ -216,8 +213,7 @@ void testDuplicateToolsInBatchListRegistration() {
216213
void testDuplicateToolsInBatchVarargsRegistration() {
217214
Tool duplicateTool = new Tool("batch-varargs-tool", "Duplicate tool in batch varargs", emptyJsonSchema);
218215

219-
assertThatThrownBy(() -> McpServer.async(createMcpTransportProvider())
220-
.serverInfo("test-server", "1.0.0")
216+
assertThatThrownBy(() -> prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
221217
.capabilities(ServerCapabilities.builder().tools(true).build())
222218
.tools(McpServerFeatures.AsyncToolSpecification.builder()
223219
.tool(duplicateTool)
@@ -236,8 +232,7 @@ void testDuplicateToolsInBatchVarargsRegistration() {
236232
void testRemoveTool() {
237233
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
238234

239-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
240-
.serverInfo("test-server", "1.0.0")
235+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
241236
.capabilities(ServerCapabilities.builder().tools(true).build())
242237
.toolCall(too, (exchange, request) -> Mono.just(new CallToolResult(List.of(), false)))
243238
.build();
@@ -249,8 +244,7 @@ void testRemoveTool() {
249244

250245
@Test
251246
void testRemoveNonexistentTool() {
252-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
253-
.serverInfo("test-server", "1.0.0")
247+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
254248
.capabilities(ServerCapabilities.builder().tools(true).build())
255249
.build();
256250

@@ -265,8 +259,7 @@ void testRemoveNonexistentTool() {
265259
void testNotifyToolsListChanged() {
266260
Tool too = new McpSchema.Tool(TEST_TOOL_NAME, "Duplicate tool", emptyJsonSchema);
267261

268-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
269-
.serverInfo("test-server", "1.0.0")
262+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
270263
.capabilities(ServerCapabilities.builder().tools(true).build())
271264
.toolCall(too, (exchange, args) -> Mono.just(new CallToolResult(List.of(), false)))
272265
.build();
@@ -282,7 +275,7 @@ void testNotifyToolsListChanged() {
282275

283276
@Test
284277
void testNotifyResourcesListChanged() {
285-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider()).serverInfo("test-server", "1.0.0").build();
278+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
286279

287280
StepVerifier.create(mcpAsyncServer.notifyResourcesListChanged()).verifyComplete();
288281

@@ -291,7 +284,7 @@ void testNotifyResourcesListChanged() {
291284

292285
@Test
293286
void testNotifyResourcesUpdated() {
294-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider()).serverInfo("test-server", "1.0.0").build();
287+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
295288

296289
StepVerifier
297290
.create(mcpAsyncServer
@@ -303,8 +296,7 @@ void testNotifyResourcesUpdated() {
303296

304297
@Test
305298
void testAddResource() {
306-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
307-
.serverInfo("test-server", "1.0.0")
299+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
308300
.capabilities(ServerCapabilities.builder().resources(true, false).build())
309301
.build();
310302

@@ -320,8 +312,7 @@ void testAddResource() {
320312

321313
@Test
322314
void testAddResourceWithNullSpecification() {
323-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
324-
.serverInfo("test-server", "1.0.0")
315+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
325316
.capabilities(ServerCapabilities.builder().resources(true, false).build())
326317
.build();
327318

@@ -336,9 +327,7 @@ void testAddResourceWithNullSpecification() {
336327
@Test
337328
void testAddResourceWithoutCapability() {
338329
// Create a server without resource capabilities
339-
McpAsyncServer serverWithoutResources = McpServer.async(createMcpTransportProvider())
340-
.serverInfo("test-server", "1.0.0")
341-
.build();
330+
McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
342331

343332
Resource resource = new Resource(TEST_RESOURCE_URI, "Test Resource", "text/plain", "Test resource description",
344333
null);
@@ -354,9 +343,7 @@ void testAddResourceWithoutCapability() {
354343
@Test
355344
void testRemoveResourceWithoutCapability() {
356345
// Create a server without resource capabilities
357-
McpAsyncServer serverWithoutResources = McpServer.async(createMcpTransportProvider())
358-
.serverInfo("test-server", "1.0.0")
359-
.build();
346+
McpAsyncServer serverWithoutResources = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
360347

361348
StepVerifier.create(serverWithoutResources.removeResource(TEST_RESOURCE_URI)).verifyErrorSatisfies(error -> {
362349
assertThat(error).isInstanceOf(McpError.class)
@@ -370,7 +357,7 @@ void testRemoveResourceWithoutCapability() {
370357

371358
@Test
372359
void testNotifyPromptsListChanged() {
373-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider()).serverInfo("test-server", "1.0.0").build();
360+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
374361

375362
StepVerifier.create(mcpAsyncServer.notifyPromptsListChanged()).verifyComplete();
376363

@@ -379,8 +366,7 @@ void testNotifyPromptsListChanged() {
379366

380367
@Test
381368
void testAddPromptWithNullSpecification() {
382-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
383-
.serverInfo("test-server", "1.0.0")
369+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
384370
.capabilities(ServerCapabilities.builder().prompts(false).build())
385371
.build();
386372

@@ -393,9 +379,7 @@ void testAddPromptWithNullSpecification() {
393379
@Test
394380
void testAddPromptWithoutCapability() {
395381
// Create a server without prompt capabilities
396-
McpAsyncServer serverWithoutPrompts = McpServer.async(createMcpTransportProvider())
397-
.serverInfo("test-server", "1.0.0")
398-
.build();
382+
McpAsyncServer serverWithoutPrompts = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
399383

400384
Prompt prompt = new Prompt(TEST_PROMPT_NAME, "Test Prompt", "Test Prompt", List.of());
401385
McpServerFeatures.AsyncPromptSpecification specification = new McpServerFeatures.AsyncPromptSpecification(
@@ -411,9 +395,7 @@ void testAddPromptWithoutCapability() {
411395
@Test
412396
void testRemovePromptWithoutCapability() {
413397
// Create a server without prompt capabilities
414-
McpAsyncServer serverWithoutPrompts = McpServer.async(createMcpTransportProvider())
415-
.serverInfo("test-server", "1.0.0")
416-
.build();
398+
McpAsyncServer serverWithoutPrompts = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
417399

418400
StepVerifier.create(serverWithoutPrompts.removePrompt(TEST_PROMPT_NAME)).verifyErrorSatisfies(error -> {
419401
assertThat(error).isInstanceOf(McpError.class)
@@ -430,8 +412,7 @@ void testRemovePrompt() {
430412
prompt, (exchange, req) -> Mono.just(new GetPromptResult("Test prompt description", List
431413
.of(new PromptMessage(McpSchema.Role.ASSISTANT, new McpSchema.TextContent("Test content"))))));
432414

433-
var mcpAsyncServer = McpServer.async(createMcpTransportProvider())
434-
.serverInfo("test-server", "1.0.0")
415+
var mcpAsyncServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
435416
.capabilities(ServerCapabilities.builder().prompts(true).build())
436417
.prompts(specification)
437418
.build();
@@ -443,8 +424,7 @@ void testRemovePrompt() {
443424

444425
@Test
445426
void testRemoveNonexistentPrompt() {
446-
var mcpAsyncServer2 = McpServer.async(createMcpTransportProvider())
447-
.serverInfo("test-server", "1.0.0")
427+
var mcpAsyncServer2 = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
448428
.capabilities(ServerCapabilities.builder().prompts(true).build())
449429
.build();
450430

@@ -467,8 +447,7 @@ void testRootsChangeHandlers() {
467447
var rootsReceived = new McpSchema.Root[1];
468448
var consumerCalled = new boolean[1];
469449

470-
var singleConsumerServer = McpServer.async(createMcpTransportProvider())
471-
.serverInfo("test-server", "1.0.0")
450+
var singleConsumerServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
472451
.rootsChangeHandlers(List.of((exchange, roots) -> Mono.fromRunnable(() -> {
473452
consumerCalled[0] = true;
474453
if (!roots.isEmpty()) {
@@ -487,8 +466,7 @@ void testRootsChangeHandlers() {
487466
var consumer2Called = new boolean[1];
488467
var rootsContent = new List[1];
489468

490-
var multipleConsumersServer = McpServer.async(createMcpTransportProvider())
491-
.serverInfo("test-server", "1.0.0")
469+
var multipleConsumersServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
492470
.rootsChangeHandlers(List.of((exchange, roots) -> Mono.fromRunnable(() -> {
493471
consumer1Called[0] = true;
494472
rootsContent[0] = roots;
@@ -501,8 +479,7 @@ void testRootsChangeHandlers() {
501479
onClose();
502480

503481
// Test error handling
504-
var errorHandlingServer = McpServer.async(createMcpTransportProvider())
505-
.serverInfo("test-server", "1.0.0")
482+
var errorHandlingServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0")
506483
.rootsChangeHandlers(List.of((exchange, roots) -> {
507484
throw new RuntimeException("Test error");
508485
}))
@@ -514,9 +491,7 @@ void testRootsChangeHandlers() {
514491
onClose();
515492

516493
// Test without consumers
517-
var noConsumersServer = McpServer.async(createMcpTransportProvider())
518-
.serverInfo("test-server", "1.0.0")
519-
.build();
494+
var noConsumersServer = prepareAsyncServerBuilder().serverInfo("test-server", "1.0.0").build();
520495

521496
assertThat(noConsumersServer).isNotNull();
522497
assertThatCode(() -> noConsumersServer.closeGracefully().block(Duration.ofSeconds(10)))

0 commit comments

Comments
 (0)