|
27 | 27 | import java.util.List; |
28 | 28 | import java.util.Map; |
29 | 29 | import java.util.Set; |
| 30 | +import java.util.stream.Stream; |
30 | 31 | import org.junit.jupiter.api.AfterEach; |
31 | 32 | import org.junit.jupiter.api.BeforeEach; |
32 | 33 | import org.junit.jupiter.api.Test; |
| 34 | +import org.junit.jupiter.params.ParameterizedTest; |
| 35 | +import org.junit.jupiter.params.provider.Arguments; |
| 36 | +import org.junit.jupiter.params.provider.MethodSource; |
33 | 37 | import software.amazon.smithy.java.core.serde.document.Document; |
34 | 38 | import software.amazon.smithy.java.io.ByteBufferUtils; |
35 | 39 | import software.amazon.smithy.java.json.JsonCodec; |
@@ -255,12 +259,18 @@ void testEchoSchemaTypeMapping() throws Exception { |
255 | 259 | assertEquals("array", echoProps.path("integerList").path("type").asText()); |
256 | 260 | assertEquals("array", echoProps.path("nestedList").path("type").asText()); |
257 | 261 |
|
258 | | - // Nested structure, map, union, and document should be objects |
| 262 | + // Nested structure, map, and union should be objects |
259 | 263 | assertEquals("object", echoProps.path("nested").path("type").asText()); |
260 | 264 | assertEquals("object", echoProps.path("stringMap").path("type").asText()); |
261 | 265 | assertEquals("object", echoProps.path("nestedMap").path("type").asText()); |
262 | 266 | assertEquals("object", echoProps.path("unionValue").path("type").asText()); |
263 | | - assertEquals("object", echoProps.path("documentValue").path("type").asText()); |
| 267 | + |
| 268 | + // Document should have type as array of all JSON types |
| 269 | + var documentType = echoProps.path("documentValue").path("type"); |
| 270 | + assertTrue(documentType.isArray(), "Document type should be an array"); |
| 271 | + var types = new HashSet<String>(); |
| 272 | + documentType.forEach(node -> types.add(node.asText())); |
| 273 | + assertEquals(Set.of("string", "number", "boolean", "object", "array", "null"), types); |
264 | 274 | } |
265 | 275 | } |
266 | 276 |
|
@@ -530,6 +540,90 @@ void testDocumentWithPrimitive() { |
530 | 540 | assertEquals("just a string", echo.getMember("documentValue").asString()); |
531 | 541 | } |
532 | 542 |
|
| 543 | + static Stream<Arguments> documentSchemaValidationTestCases() { |
| 544 | + return Stream.of( |
| 545 | + // String document |
| 546 | + Arguments.of("string document", Document.of("hello world")), |
| 547 | + // Number document (integer) |
| 548 | + Arguments.of("integer document", Document.of(42)), |
| 549 | + // Number document (double) |
| 550 | + Arguments.of("double document", Document.of(3.14159)), |
| 551 | + // Boolean document (true) |
| 552 | + Arguments.of("boolean true document", Document.of(true)), |
| 553 | + // Boolean document (false) |
| 554 | + Arguments.of("boolean false document", Document.of(false)), |
| 555 | + // Array document |
| 556 | + Arguments.of("array document", |
| 557 | + Document.of(List.of( |
| 558 | + Document.of("a"), |
| 559 | + Document.of(1), |
| 560 | + Document.of(true)))), |
| 561 | + // Object document |
| 562 | + Arguments.of("object document", |
| 563 | + Document.of(Map.of( |
| 564 | + "key", |
| 565 | + Document.of("value"), |
| 566 | + "number", |
| 567 | + Document.of(123)))), |
| 568 | + // Nested object document |
| 569 | + Arguments.of("nested object document", |
| 570 | + Document.of(Map.of( |
| 571 | + "outer", |
| 572 | + Document.of(Map.of( |
| 573 | + "inner", |
| 574 | + Document.of("deep value")))))), |
| 575 | + // Mixed array document |
| 576 | + Arguments.of("mixed array document", |
| 577 | + Document.of(List.of( |
| 578 | + Document.of("string"), |
| 579 | + Document.of(42), |
| 580 | + Document.of(true), |
| 581 | + Document.of(Map.of("nested", Document.of("value"))), |
| 582 | + Document.of(List.of(Document.of(1), Document.of(2)))))), |
| 583 | + // Empty object document |
| 584 | + Arguments.of("empty object document", Document.of(Map.of()))); |
| 585 | + } |
| 586 | + |
| 587 | + @ParameterizedTest(name = "{0}") |
| 588 | + @MethodSource("documentSchemaValidationTestCases") |
| 589 | + void testDocumentValidatesAgainstSchema(String description, Document documentValue) throws Exception { |
| 590 | + initializeLatestProtocol(); |
| 591 | + |
| 592 | + // Get output schema from raw JSON (using Jackson directly) |
| 593 | + write("tools/list", Document.of(Map.of())); |
| 594 | + var toolsResponseJson = readRawResponse(); |
| 595 | + var toolsResponseNode = OBJECT_MAPPER.readTree(toolsResponseJson); |
| 596 | + var outputSchemaNode = toolsResponseNode |
| 597 | + .path("result") |
| 598 | + .path("tools") |
| 599 | + .get(0) |
| 600 | + .path("outputSchema"); |
| 601 | + |
| 602 | + // Create input with the document value |
| 603 | + var echoData = new HashMap<String, Document>(); |
| 604 | + echoData.put("requiredField", Document.of("required")); |
| 605 | + echoData.put("documentValue", documentValue); |
| 606 | + |
| 607 | + // Call tool and get raw JSON response (using Jackson directly) |
| 608 | + var params = Document.of(Map.of( |
| 609 | + "name", |
| 610 | + Document.of("McpEcho"), |
| 611 | + "arguments", |
| 612 | + createEchoInput(echoData))); |
| 613 | + write("tools/call", params); |
| 614 | + var callResponseJson = readRawResponse(); |
| 615 | + var callResponseNode = OBJECT_MAPPER.readTree(callResponseJson); |
| 616 | + var structuredContentNode = callResponseNode.path("result").path("structuredContent"); |
| 617 | + |
| 618 | + assertNotNull(structuredContentNode, "Structured content should not be null for: " + description); |
| 619 | + |
| 620 | + // Validate using Jackson-parsed JSON directly |
| 621 | + JsonSchema schema = SCHEMA_FACTORY.getSchema(outputSchemaNode); |
| 622 | + Set<ValidationMessage> errors = schema.validate(structuredContentNode); |
| 623 | + assertTrue(errors.isEmpty(), |
| 624 | + "Validation errors for " + description + ": " + errors); |
| 625 | + } |
| 626 | + |
533 | 627 | // ========== Enum Tests ========== |
534 | 628 |
|
535 | 629 | @Test |
|
0 commit comments