|
1 | 1 | package io.modelcontextprotocol.kotlin.sdk.integration.kotlin |
2 | 2 |
|
| 3 | +import io.kotest.assertions.json.shouldEqualJson |
3 | 4 | import io.modelcontextprotocol.kotlin.sdk.CallToolRequest |
4 | 5 | import io.modelcontextprotocol.kotlin.sdk.CallToolResult |
5 | 6 | import io.modelcontextprotocol.kotlin.sdk.CallToolResultBase |
6 | 7 | import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities |
7 | 8 | import io.modelcontextprotocol.kotlin.sdk.TextContent |
8 | 9 | import io.modelcontextprotocol.kotlin.sdk.Tool |
9 | | -import io.modelcontextprotocol.kotlin.sdk.integration.utils.TestUtils.assertCallToolResult |
10 | | -import io.modelcontextprotocol.kotlin.sdk.integration.utils.TestUtils.assertJsonEquals |
11 | | -import io.modelcontextprotocol.kotlin.sdk.integration.utils.TestUtils.assertTextContent |
12 | 10 | import io.modelcontextprotocol.kotlin.sdk.integration.utils.TestUtils.runTest |
13 | 11 | import kotlinx.coroutines.delay |
14 | 12 | import kotlinx.coroutines.launch |
@@ -281,143 +279,150 @@ class ToolEdgeCasesTest : KotlinTestBase() { |
281 | 279 | } |
282 | 280 |
|
283 | 281 | @Test |
284 | | - fun testBasicTool() = runTest { |
285 | | - val testText = "Hello, world!" |
286 | | - val arguments = mapOf("text" to testText) |
| 282 | + fun testBasicTool() { |
| 283 | + runTest { |
| 284 | + val testText = "Hello, world!" |
| 285 | + val arguments = mapOf("text" to testText) |
287 | 286 |
|
288 | | - val result = client.callTool(basicToolName, arguments) |
| 287 | + val result = client.callTool(basicToolName, arguments) as CallToolResultBase |
289 | 288 |
|
290 | | - val toolResult = assertCallToolResult(result) |
291 | | - assertTextContent(toolResult.content.firstOrNull(), "Echo: $testText") |
| 289 | + val expectedToolResult = "[TextContent(text=Echo: Hello, world!, annotations=null)]" |
| 290 | + assertEquals(expectedToolResult, result.content.toString(), "Unexpected tool result") |
292 | 291 |
|
293 | | - val structuredContent = toolResult.structuredContent as JsonObject |
294 | | - val expected = buildJsonObject { put("result", testText) } |
295 | | - assertJsonEquals(expected, structuredContent) |
| 292 | + val actualContent = result.structuredContent.toString() |
| 293 | + val expectedContent = """ |
| 294 | + { |
| 295 | + "result" : "Hello, world!" |
| 296 | + } |
| 297 | + """.trimIndent() |
| 298 | + |
| 299 | + actualContent.shouldEqualJson(expectedContent) |
| 300 | + } |
296 | 301 | } |
297 | 302 |
|
298 | 303 | @Test |
299 | | - fun testComplexNestedSchema() = runTest { |
300 | | - val userJson = buildJsonObject { |
301 | | - put("name", JsonPrimitive("John Doe")) |
302 | | - put("age", JsonPrimitive(30)) |
303 | | - put( |
304 | | - "address", |
305 | | - buildJsonObject { |
306 | | - put("street", JsonPrimitive("123 Main St")) |
307 | | - put("city", JsonPrimitive("New York")) |
308 | | - put("country", JsonPrimitive("USA")) |
309 | | - }, |
310 | | - ) |
311 | | - } |
| 304 | + fun testComplexNestedSchema() { |
| 305 | + runTest { |
| 306 | + val userJson = buildJsonObject { |
| 307 | + put("name", JsonPrimitive("John Galt")) |
| 308 | + put("age", JsonPrimitive(30)) |
| 309 | + put( |
| 310 | + "address", |
| 311 | + buildJsonObject { |
| 312 | + put("street", JsonPrimitive("123 Main St")) |
| 313 | + put("city", JsonPrimitive("New York")) |
| 314 | + put("country", JsonPrimitive("USA")) |
| 315 | + }, |
| 316 | + ) |
| 317 | + } |
312 | 318 |
|
313 | | - val optionsJson = buildJsonArray { |
314 | | - add(JsonPrimitive("option1")) |
315 | | - add(JsonPrimitive("option2")) |
316 | | - add(JsonPrimitive("option3")) |
317 | | - } |
| 319 | + val optionsJson = buildJsonArray { |
| 320 | + add(JsonPrimitive("option1")) |
| 321 | + add(JsonPrimitive("option2")) |
| 322 | + add(JsonPrimitive("option3")) |
| 323 | + } |
318 | 324 |
|
319 | | - val arguments = buildJsonObject { |
320 | | - put("user", userJson) |
321 | | - put("options", optionsJson) |
322 | | - } |
| 325 | + val arguments = buildJsonObject { |
| 326 | + put("user", userJson) |
| 327 | + put("options", optionsJson) |
| 328 | + } |
323 | 329 |
|
324 | | - val result = client.callTool( |
325 | | - CallToolRequest( |
326 | | - name = complexToolName, |
327 | | - arguments = arguments, |
328 | | - ), |
329 | | - ) |
330 | | - |
331 | | - val toolResult = assertCallToolResult(result) |
332 | | - val content = toolResult.content.firstOrNull() as? TextContent |
333 | | - assertNotNull(content, "Tool result content should be TextContent") |
334 | | - val text = content.text ?: "" |
335 | | - |
336 | | - assertTrue(text.contains("John Doe"), "Result should contain the name") |
337 | | - assertTrue(text.contains("30"), "Result should contain the age") |
338 | | - assertTrue(text.contains("123 Main St"), "Result should contain the street") |
339 | | - assertTrue(text.contains("New York"), "Result should contain the city") |
340 | | - assertTrue(text.contains("USA"), "Result should contain the country") |
341 | | - assertTrue(text.contains("option1"), "Result should contain option1") |
342 | | - assertTrue(text.contains("option2"), "Result should contain option2") |
343 | | - assertTrue(text.contains("option3"), "Result should contain option3") |
344 | | - |
345 | | - val structuredContent = toolResult.structuredContent as JsonObject |
346 | | - val expectedStructured = buildJsonObject { |
347 | | - put("name", "John Doe") |
348 | | - put("age", 30) |
349 | | - put("address", buildJsonObject { |
350 | | - put("street", "123 Main St") |
351 | | - put("city", "New York") |
352 | | - put("country", "USA") |
353 | | - }) |
354 | | - put("options", buildJsonArray { |
355 | | - add("option1"); add("option2"); add("option3") |
356 | | - }) |
| 330 | + val result = client.callTool( |
| 331 | + CallToolRequest( |
| 332 | + name = complexToolName, |
| 333 | + arguments = arguments, |
| 334 | + ), |
| 335 | + ) as CallToolResultBase |
| 336 | + |
| 337 | + val actualContent = result.structuredContent.toString() |
| 338 | + val expectedContent = """ |
| 339 | + { |
| 340 | + "name" : "John Galt", |
| 341 | + "age" : 30, |
| 342 | + "address" : { |
| 343 | + "street" : "123 Main St", |
| 344 | + "city" : "New York", |
| 345 | + "country" : "USA" |
| 346 | + }, |
| 347 | + "options" : [ "option1", "option2", "option3" ] |
| 348 | + } |
| 349 | + """.trimIndent() |
| 350 | + |
| 351 | + actualContent.shouldEqualJson(expectedContent) |
357 | 352 | } |
358 | | - assertJsonEquals(expectedStructured, structuredContent) |
359 | 353 | } |
360 | 354 |
|
361 | 355 | @Test |
362 | | - fun testLargeResponse() = runTest { |
363 | | - val size = 10 |
364 | | - val arguments = mapOf("size" to size) |
| 356 | + fun testLargeResponse() { |
| 357 | + runTest { |
| 358 | + val size = 10 |
| 359 | + val arguments = mapOf("size" to size) |
365 | 360 |
|
366 | | - val result = client.callTool(largeToolName, arguments) |
| 361 | + val result = client.callTool(largeToolName, arguments) as CallToolResultBase |
367 | 362 |
|
368 | | - val toolResult = assertCallToolResult(result) |
369 | | - val content = toolResult.content.firstOrNull() as? TextContent |
370 | | - assertNotNull(content, "Tool result content should be TextContent") |
371 | | - val text = content.text ?: "" |
| 363 | + val content = result.content.firstOrNull() as TextContent |
| 364 | + assertNotNull(content, "Tool result content should be TextContent") |
372 | 365 |
|
373 | | - assertEquals(10000, text.length, "Response should be 10KB in size") |
| 366 | + val actualContent = result.structuredContent.toString() |
| 367 | + val expectedContent = """ |
| 368 | + { |
| 369 | + "size" : 10000 |
| 370 | + } |
| 371 | + """.trimIndent() |
374 | 372 |
|
375 | | - val structuredContent = toolResult.structuredContent as JsonObject |
376 | | - val expected = buildJsonObject { put("size", 10000) } |
377 | | - assertJsonEquals(expected, structuredContent) |
| 373 | + actualContent.shouldEqualJson(expectedContent) |
| 374 | + } |
378 | 375 | } |
379 | 376 |
|
380 | 377 | @Test |
381 | | - fun testSlowTool() = runTest { |
382 | | - val delay = 500 |
383 | | - val arguments = mapOf("delay" to delay) |
| 378 | + fun testSlowTool() { |
| 379 | + runTest { |
| 380 | + val delay = 500 |
| 381 | + val arguments = mapOf("delay" to delay) |
384 | 382 |
|
385 | | - val startTime = System.currentTimeMillis() |
386 | | - val result = client.callTool(slowToolName, arguments) |
387 | | - val endTime = System.currentTimeMillis() |
| 383 | + val startTime = System.currentTimeMillis() |
| 384 | + val result = client.callTool(slowToolName, arguments) as CallToolResultBase |
| 385 | + val endTime = System.currentTimeMillis() |
388 | 386 |
|
389 | | - val toolResult = assertCallToolResult(result) |
390 | | - val content = toolResult.content.firstOrNull() as? TextContent |
391 | | - assertNotNull(content, "Tool result content should be TextContent") |
392 | | - val text = content.text ?: "" |
| 387 | + val content = result.content.firstOrNull() as? TextContent |
| 388 | + assertNotNull(content, "Tool result content should be TextContent") |
393 | 389 |
|
394 | | - assertTrue(text.contains("${delay}ms"), "Result should mention the delay") |
395 | | - assertTrue(endTime - startTime >= delay, "Tool should take at least the specified delay") |
| 390 | + assertTrue(endTime - startTime >= delay, "Tool should take at least the specified delay") |
396 | 391 |
|
397 | | - val structuredContent = toolResult.structuredContent as JsonObject |
398 | | - val expected = buildJsonObject { put("delay", delay) } |
399 | | - assertJsonEquals(expected, structuredContent) |
| 392 | + val actualContent = result.structuredContent.toString() |
| 393 | + val expectedContent = """ |
| 394 | + { |
| 395 | + "delay" : 500 |
| 396 | + } |
| 397 | + """.trimIndent() |
| 398 | + |
| 399 | + actualContent.shouldEqualJson(expectedContent) |
| 400 | + } |
400 | 401 | } |
401 | 402 |
|
402 | 403 | @Test |
403 | | - fun testSpecialCharacters() = runTest { |
404 | | - val arguments = mapOf("special" to specialCharsContent) |
| 404 | + fun testSpecialCharacters() { |
| 405 | + runTest { |
| 406 | + val arguments = mapOf("special" to specialCharsContent) |
405 | 407 |
|
406 | | - val result = client.callTool(specialCharsToolName, arguments) |
| 408 | + val result = client.callTool(specialCharsToolName, arguments) as CallToolResultBase |
407 | 409 |
|
408 | | - val toolResult = assertCallToolResult(result) |
409 | | - val content = toolResult.content.firstOrNull() as? TextContent |
410 | | - assertNotNull(content, "Tool result content should be TextContent") |
411 | | - val text = content.text ?: "" |
| 410 | + val content = result.content.firstOrNull() as? TextContent |
| 411 | + assertNotNull(content, "Tool result content should be TextContent") |
| 412 | + val text = content.text ?: "" |
412 | 413 |
|
413 | | - assertTrue(text.contains(specialCharsContent), "Result should contain the special characters") |
| 414 | + assertTrue(text.contains(specialCharsContent), "Result should contain the special characters") |
| 415 | + |
| 416 | + val actualContent = result.structuredContent.toString() |
| 417 | + val expectedContent = """ |
| 418 | + { |
| 419 | + "special" : "!@#$%^&*()_+{}|:\"<>?~`-=[]\\;',./\n\t", |
| 420 | + "length" : 34 |
| 421 | + } |
| 422 | + """.trimIndent() |
414 | 423 |
|
415 | | - val structuredContent = toolResult.structuredContent as JsonObject |
416 | | - val expected = buildJsonObject { |
417 | | - put("special", specialCharsContent) |
418 | | - put("length", specialCharsContent.length) |
| 424 | + actualContent.shouldEqualJson(expectedContent) |
419 | 425 | } |
420 | | - assertJsonEquals(expected, structuredContent) |
421 | 426 | } |
422 | 427 |
|
423 | 428 | @Test |
|
0 commit comments