|
2 | 2 | * Copyright 2024-2024 the original author or authors. |
3 | 3 | */ |
4 | 4 |
|
5 | | -package io.modelcontextprotocol.spec; |
| 5 | +package io.modelcontextprotocol.json; |
6 | 6 |
|
7 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; |
8 | 8 | import static org.junit.jupiter.api.Assertions.assertFalse; |
|
13 | 13 | import static org.mockito.ArgumentMatchers.any; |
14 | 14 | import static org.mockito.Mockito.when; |
15 | 15 |
|
| 16 | +import java.util.List; |
16 | 17 | import java.util.Map; |
17 | 18 | import java.util.stream.Stream; |
18 | 19 |
|
@@ -64,6 +65,16 @@ private Map<String, Object> toMap(String json) { |
64 | 65 | } |
65 | 66 | } |
66 | 67 |
|
| 68 | + private List<Map<String, Object>> toListMap(String json) { |
| 69 | + try { |
| 70 | + return objectMapper.readValue(json, new TypeReference<List<Map<String, Object>>>() { |
| 71 | + }); |
| 72 | + } |
| 73 | + catch (Exception e) { |
| 74 | + throw new RuntimeException("Failed to parse JSON: " + json, e); |
| 75 | + } |
| 76 | + } |
| 77 | + |
67 | 78 | @Test |
68 | 79 | void testDefaultConstructor() { |
69 | 80 | DefaultJsonSchemaValidator defaultValidator = new DefaultJsonSchemaValidator(); |
@@ -198,6 +209,74 @@ void testValidateWithValidArraySchema() { |
198 | 209 | assertNull(response.errorMessage()); |
199 | 210 | } |
200 | 211 |
|
| 212 | + @Test |
| 213 | + void testValidateWithValidArraySchemaTopLevelArray() { |
| 214 | + String schemaJson = """ |
| 215 | + { |
| 216 | + "$schema" : "https://json-schema.org/draft/2020-12/schema", |
| 217 | + "type" : "array", |
| 218 | + "items" : { |
| 219 | + "type" : "object", |
| 220 | + "properties" : { |
| 221 | + "city" : { |
| 222 | + "type" : "string" |
| 223 | + }, |
| 224 | + "summary" : { |
| 225 | + "type" : "string" |
| 226 | + }, |
| 227 | + "temperatureC" : { |
| 228 | + "type" : "number", |
| 229 | + "format" : "float" |
| 230 | + } |
| 231 | + }, |
| 232 | + "required" : [ "city", "summary", "temperatureC" ] |
| 233 | + }, |
| 234 | + "additionalProperties" : false |
| 235 | + } |
| 236 | + """; |
| 237 | + |
| 238 | + String contentJson = """ |
| 239 | + [ |
| 240 | + { |
| 241 | + "city": "London", |
| 242 | + "summary": "Generally mild with frequent rainfall. Winters are cool and damp, summers are warm but rarely hot. Cloudy conditions are common throughout the year.", |
| 243 | + "temperatureC": 11.3 |
| 244 | + }, |
| 245 | + { |
| 246 | + "city": "New York", |
| 247 | + "summary": "Four distinct seasons with hot and humid summers, cold winters with snow, and mild springs and autumns. Precipitation is fairly evenly distributed throughout the year.", |
| 248 | + "temperatureC": 12.8 |
| 249 | + }, |
| 250 | + { |
| 251 | + "city": "San Francisco", |
| 252 | + "summary": "Mild year-round with a distinctive Mediterranean climate. Famous for summer fog, mild winters, and little temperature variation throughout the year. Very little rainfall in summer months.", |
| 253 | + "temperatureC": 14.6 |
| 254 | + }, |
| 255 | + { |
| 256 | + "city": "Tokyo", |
| 257 | + "summary": "Humid subtropical climate with hot, wet summers and mild winters. Experiences a rainy season in early summer and occasional typhoons in late summer to early autumn.", |
| 258 | + "temperatureC": 15.4 |
| 259 | + } |
| 260 | + ] |
| 261 | + """; |
| 262 | + |
| 263 | + Map<String, Object> schema = toMap(schemaJson); |
| 264 | + |
| 265 | + // Validate as JSON string |
| 266 | + ValidationResponse response = validator.validate(schema, contentJson); |
| 267 | + |
| 268 | + assertTrue(response.valid()); |
| 269 | + assertNull(response.errorMessage()); |
| 270 | + |
| 271 | + List<Map<String, Object>> structuredContent = toListMap(contentJson); |
| 272 | + |
| 273 | + // Validate as List<Map<String, Object>> |
| 274 | + response = validator.validate(schema, structuredContent); |
| 275 | + |
| 276 | + assertTrue(response.valid()); |
| 277 | + assertNull(response.errorMessage()); |
| 278 | + } |
| 279 | + |
201 | 280 | @Test |
202 | 281 | void testValidateWithInvalidTypeSchema() { |
203 | 282 | String schemaJson = """ |
@@ -266,7 +345,8 @@ void testValidateWithAdditionalPropertiesNotAllowed() { |
266 | 345 | "properties": { |
267 | 346 | "name": {"type": "string"} |
268 | 347 | }, |
269 | | - "required": ["name"] |
| 348 | + "required": ["name"], |
| 349 | + "additionalProperties": false |
270 | 350 | } |
271 | 351 | """; |
272 | 352 |
|
@@ -316,6 +396,35 @@ void testValidateWithAdditionalPropertiesExplicitlyAllowed() { |
316 | 396 | assertNull(response.errorMessage()); |
317 | 397 | } |
318 | 398 |
|
| 399 | + @Test |
| 400 | + void testValidateWithDefaultAdditionalProperties() { |
| 401 | + String schemaJson = """ |
| 402 | + { |
| 403 | + "type": "object", |
| 404 | + "properties": { |
| 405 | + "name": {"type": "string"} |
| 406 | + }, |
| 407 | + "required": ["name"], |
| 408 | + "additionalProperties": true |
| 409 | + } |
| 410 | + """; |
| 411 | + |
| 412 | + String contentJson = """ |
| 413 | + { |
| 414 | + "name": "John Doe", |
| 415 | + "extraField": "should be allowed" |
| 416 | + } |
| 417 | + """; |
| 418 | + |
| 419 | + Map<String, Object> schema = toMap(schemaJson); |
| 420 | + Map<String, Object> structuredContent = toMap(contentJson); |
| 421 | + |
| 422 | + ValidationResponse response = validator.validate(schema, structuredContent); |
| 423 | + |
| 424 | + assertTrue(response.valid()); |
| 425 | + assertNull(response.errorMessage()); |
| 426 | + } |
| 427 | + |
319 | 428 | @Test |
320 | 429 | void testValidateWithAdditionalPropertiesExplicitlyDisallowed() { |
321 | 430 | String schemaJson = """ |
|
0 commit comments