]>
Commit | Line | Data |
---|---|---|
31f18b77 FG |
1 | // Tencent is pleased to support the open source community by making RapidJSON available. |
2 | // | |
3 | // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. | |
4 | // | |
5 | // Licensed under the MIT License (the "License"); you may not use this file except | |
6 | // in compliance with the License. You may obtain a copy of the License at | |
7 | // | |
8 | // http://opensource.org/licenses/MIT | |
9 | // | |
10 | // Unless required by applicable law or agreed to in writing, software distributed | |
11 | // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR | |
12 | // CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
13 | // specific language governing permissions and limitations under the License. | |
14 | ||
15 | #include "unittest.h" | |
16 | #include "rapidjson/schema.h" | |
17 | #include "rapidjson/stringbuffer.h" | |
18 | #include "rapidjson/writer.h" | |
19 | ||
20 | #ifdef __clang__ | |
21 | RAPIDJSON_DIAG_PUSH | |
22 | RAPIDJSON_DIAG_OFF(variadic-macros) | |
23 | #endif | |
24 | ||
25 | using namespace rapidjson; | |
26 | ||
27 | #define TEST_HASHER(json1, json2, expected) \ | |
28 | {\ | |
29 | Document d1, d2;\ | |
30 | d1.Parse(json1);\ | |
31 | ASSERT_FALSE(d1.HasParseError());\ | |
32 | d2.Parse(json2);\ | |
33 | ASSERT_FALSE(d2.HasParseError());\ | |
34 | internal::Hasher<Value, CrtAllocator> h1, h2;\ | |
35 | d1.Accept(h1);\ | |
36 | d2.Accept(h2);\ | |
37 | ASSERT_TRUE(h1.IsValid());\ | |
38 | ASSERT_TRUE(h2.IsValid());\ | |
39 | /*printf("%s: 0x%016llx\n%s: 0x%016llx\n\n", json1, h1.GetHashCode(), json2, h2.GetHashCode());*/\ | |
40 | EXPECT_TRUE(expected == (h1.GetHashCode() == h2.GetHashCode()));\ | |
41 | } | |
42 | ||
43 | TEST(SchemaValidator, Hasher) { | |
44 | TEST_HASHER("null", "null", true); | |
45 | ||
46 | TEST_HASHER("true", "true", true); | |
47 | TEST_HASHER("false", "false", true); | |
48 | TEST_HASHER("true", "false", false); | |
49 | TEST_HASHER("false", "true", false); | |
50 | TEST_HASHER("true", "null", false); | |
51 | TEST_HASHER("false", "null", false); | |
52 | ||
53 | TEST_HASHER("1", "1", true); | |
54 | TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned | |
55 | TEST_HASHER("-2147483649", "-2147483649", true); // -2^31 - 1 can only be fit in int64_t | |
56 | TEST_HASHER("2147483648", "2147483648", true); // 2^31 can only be fit in unsigned | |
57 | TEST_HASHER("4294967296", "4294967296", true); // 2^32 can only be fit in int64_t | |
58 | TEST_HASHER("9223372036854775808", "9223372036854775808", true); // 2^63 can only be fit in uint64_t | |
59 | TEST_HASHER("1.5", "1.5", true); | |
60 | TEST_HASHER("1", "1.0", true); | |
61 | TEST_HASHER("1", "-1", false); | |
62 | TEST_HASHER("0.0", "-0.0", false); | |
63 | TEST_HASHER("1", "true", false); | |
64 | TEST_HASHER("0", "false", false); | |
65 | TEST_HASHER("0", "null", false); | |
66 | ||
67 | TEST_HASHER("\"\"", "\"\"", true); | |
68 | TEST_HASHER("\"\"", "\"\\u0000\"", false); | |
69 | TEST_HASHER("\"Hello\"", "\"Hello\"", true); | |
70 | TEST_HASHER("\"Hello\"", "\"World\"", false); | |
71 | TEST_HASHER("\"Hello\"", "null", false); | |
72 | TEST_HASHER("\"Hello\\u0000\"", "\"Hello\"", false); | |
73 | TEST_HASHER("\"\"", "null", false); | |
74 | TEST_HASHER("\"\"", "true", false); | |
75 | TEST_HASHER("\"\"", "false", false); | |
76 | ||
77 | TEST_HASHER("[]", "[ ]", true); | |
78 | TEST_HASHER("[1, true, false]", "[1, true, false]", true); | |
79 | TEST_HASHER("[1, true, false]", "[1, true]", false); | |
80 | TEST_HASHER("[1, 2]", "[2, 1]", false); | |
81 | TEST_HASHER("[[1], 2]", "[[1, 2]]", false); | |
82 | TEST_HASHER("[1, 2]", "[1, [2]]", false); | |
83 | TEST_HASHER("[]", "null", false); | |
84 | TEST_HASHER("[]", "true", false); | |
85 | TEST_HASHER("[]", "false", false); | |
86 | TEST_HASHER("[]", "0", false); | |
87 | TEST_HASHER("[]", "0.0", false); | |
88 | TEST_HASHER("[]", "\"\"", false); | |
89 | ||
90 | TEST_HASHER("{}", "{ }", true); | |
91 | TEST_HASHER("{\"a\":1}", "{\"a\":1}", true); | |
92 | TEST_HASHER("{\"a\":1}", "{\"b\":1}", false); | |
93 | TEST_HASHER("{\"a\":1}", "{\"a\":2}", false); | |
94 | TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive | |
95 | TEST_HASHER("{}", "null", false); | |
96 | TEST_HASHER("{}", "false", false); | |
97 | TEST_HASHER("{}", "true", false); | |
98 | TEST_HASHER("{}", "0", false); | |
99 | TEST_HASHER("{}", "0.0", false); | |
100 | TEST_HASHER("{}", "\"\"", false); | |
101 | } | |
102 | ||
103 | // Test cases following http://spacetelescope.github.io/understanding-json-schema | |
104 | ||
105 | #define VALIDATE(schema, json, expected) \ | |
106 | {\ | |
107 | SchemaValidator validator(schema);\ | |
108 | Document d;\ | |
109 | /*printf("\n%s\n", json);*/\ | |
110 | d.Parse(json);\ | |
111 | EXPECT_FALSE(d.HasParseError());\ | |
112 | EXPECT_TRUE(expected == d.Accept(validator));\ | |
113 | EXPECT_TRUE(expected == validator.IsValid());\ | |
114 | if ((expected) && !validator.IsValid()) {\ | |
115 | StringBuffer sb;\ | |
116 | validator.GetInvalidSchemaPointer().StringifyUriFragment(sb);\ | |
117 | printf("Invalid schema: %s\n", sb.GetString());\ | |
118 | printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword());\ | |
119 | sb.Clear();\ | |
120 | validator.GetInvalidDocumentPointer().StringifyUriFragment(sb);\ | |
121 | printf("Invalid document: %s\n", sb.GetString());\ | |
122 | }\ | |
123 | } | |
124 | ||
125 | #define INVALIDATE(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer) \ | |
126 | {\ | |
127 | SchemaValidator validator(schema);\ | |
128 | Document d;\ | |
129 | /*printf("\n%s\n", json);*/\ | |
130 | d.Parse(json);\ | |
131 | EXPECT_FALSE(d.HasParseError());\ | |
132 | EXPECT_FALSE(d.Accept(validator));\ | |
133 | EXPECT_FALSE(validator.IsValid());\ | |
134 | if (validator.GetInvalidSchemaPointer() != Pointer(invalidSchemaPointer)) {\ | |
135 | StringBuffer sb;\ | |
136 | validator.GetInvalidSchemaPointer().Stringify(sb);\ | |
137 | printf("GetInvalidSchemaPointer() Expected: %s Actual: %s\n", invalidSchemaPointer, sb.GetString());\ | |
138 | ADD_FAILURE();\ | |
139 | }\ | |
140 | ASSERT_TRUE(validator.GetInvalidSchemaKeyword() != 0);\ | |
141 | if (strcmp(validator.GetInvalidSchemaKeyword(), invalidSchemaKeyword) != 0) {\ | |
142 | printf("GetInvalidSchemaKeyword() Expected: %s Actual %s\n", invalidSchemaKeyword, validator.GetInvalidSchemaKeyword());\ | |
143 | ADD_FAILURE();\ | |
144 | }\ | |
145 | if (validator.GetInvalidDocumentPointer() != Pointer(invalidDocumentPointer)) {\ | |
146 | StringBuffer sb;\ | |
147 | validator.GetInvalidDocumentPointer().Stringify(sb);\ | |
148 | printf("GetInvalidDocumentPointer() Expected: %s Actual: %s\n", invalidDocumentPointer, sb.GetString());\ | |
149 | ADD_FAILURE();\ | |
150 | }\ | |
151 | } | |
152 | ||
153 | TEST(SchemaValidator, Typeless) { | |
154 | Document sd; | |
155 | sd.Parse("{}"); | |
156 | SchemaDocument s(sd); | |
157 | ||
158 | VALIDATE(s, "42", true); | |
159 | VALIDATE(s, "\"I'm a string\"", true); | |
160 | VALIDATE(s, "{ \"an\": [ \"arbitrarily\", \"nested\" ], \"data\": \"structure\" }", true); | |
161 | } | |
162 | ||
163 | TEST(SchemaValidator, MultiType) { | |
164 | Document sd; | |
165 | sd.Parse("{ \"type\": [\"number\", \"string\"] }"); | |
166 | SchemaDocument s(sd); | |
167 | ||
168 | VALIDATE(s, "42", true); | |
169 | VALIDATE(s, "\"Life, the universe, and everything\"", true); | |
170 | INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", ""); | |
171 | } | |
172 | ||
173 | TEST(SchemaValidator, Enum_Typed) { | |
174 | Document sd; | |
175 | sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }"); | |
176 | SchemaDocument s(sd); | |
177 | ||
178 | VALIDATE(s, "\"red\"", true); | |
179 | INVALIDATE(s, "\"blue\"", "", "enum", ""); | |
180 | } | |
181 | ||
182 | TEST(SchemaValidator, Enum_Typless) { | |
183 | Document sd; | |
184 | sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }"); | |
185 | SchemaDocument s(sd); | |
186 | ||
187 | VALIDATE(s, "\"red\"", true); | |
188 | VALIDATE(s, "null", true); | |
189 | VALIDATE(s, "42", true); | |
190 | INVALIDATE(s, "0", "", "enum", ""); | |
191 | } | |
192 | ||
193 | TEST(SchemaValidator, Enum_InvalidType) { | |
194 | Document sd; | |
195 | sd.Parse("{ \"type\": \"string\", \"enum\": [\"red\", \"amber\", \"green\", null] }"); | |
196 | SchemaDocument s(sd); | |
197 | ||
198 | VALIDATE(s, "\"red\"", true); | |
199 | INVALIDATE(s, "null", "", "type", ""); | |
200 | } | |
201 | ||
202 | TEST(SchemaValidator, AllOf) { | |
203 | { | |
204 | Document sd; | |
205 | sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"string\", \"maxLength\": 5 }]}"); | |
206 | SchemaDocument s(sd); | |
207 | ||
208 | VALIDATE(s, "\"ok\"", true); | |
209 | INVALIDATE(s, "\"too long\"", "", "allOf", ""); | |
210 | } | |
211 | { | |
212 | Document sd; | |
213 | sd.Parse("{\"allOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }"); | |
214 | SchemaDocument s(sd); | |
215 | ||
216 | VALIDATE(s, "\"No way\"", false); | |
217 | INVALIDATE(s, "-1", "", "allOf", ""); | |
218 | } | |
219 | } | |
220 | ||
221 | TEST(SchemaValidator, AnyOf) { | |
222 | Document sd; | |
223 | sd.Parse("{\"anyOf\": [{ \"type\": \"string\" }, { \"type\": \"number\" } ] }"); | |
224 | SchemaDocument s(sd); | |
225 | ||
226 | VALIDATE(s, "\"Yes\"", true); | |
227 | VALIDATE(s, "42", true); | |
228 | INVALIDATE(s, "{ \"Not a\": \"string or number\" }", "", "anyOf", ""); | |
229 | } | |
230 | ||
231 | TEST(SchemaValidator, OneOf) { | |
232 | Document sd; | |
233 | sd.Parse("{\"oneOf\": [{ \"type\": \"number\", \"multipleOf\": 5 }, { \"type\": \"number\", \"multipleOf\": 3 } ] }"); | |
234 | SchemaDocument s(sd); | |
235 | ||
236 | VALIDATE(s, "10", true); | |
237 | VALIDATE(s, "9", true); | |
238 | INVALIDATE(s, "2", "", "oneOf", ""); | |
239 | INVALIDATE(s, "15", "", "oneOf", ""); | |
240 | } | |
241 | ||
242 | TEST(SchemaValidator, Not) { | |
243 | Document sd; | |
244 | sd.Parse("{\"not\":{ \"type\": \"string\"}}"); | |
245 | SchemaDocument s(sd); | |
246 | ||
247 | VALIDATE(s, "42", true); | |
248 | VALIDATE(s, "{ \"key\": \"value\" }", true); | |
249 | INVALIDATE(s, "\"I am a string\"", "", "not", ""); | |
250 | } | |
251 | ||
252 | TEST(SchemaValidator, Ref) { | |
253 | Document sd; | |
254 | sd.Parse( | |
255 | "{" | |
256 | " \"$schema\": \"http://json-schema.org/draft-04/schema#\"," | |
257 | "" | |
258 | " \"definitions\": {" | |
259 | " \"address\": {" | |
260 | " \"type\": \"object\"," | |
261 | " \"properties\": {" | |
262 | " \"street_address\": { \"type\": \"string\" }," | |
263 | " \"city\": { \"type\": \"string\" }," | |
264 | " \"state\": { \"type\": \"string\" }" | |
265 | " }," | |
266 | " \"required\": [\"street_address\", \"city\", \"state\"]" | |
267 | " }" | |
268 | " }," | |
269 | " \"type\": \"object\"," | |
270 | " \"properties\": {" | |
271 | " \"billing_address\": { \"$ref\": \"#/definitions/address\" }," | |
272 | " \"shipping_address\": { \"$ref\": \"#/definitions/address\" }" | |
273 | " }" | |
274 | "}"); | |
275 | SchemaDocument s(sd); | |
276 | ||
277 | VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"}, \"billing_address\": {\"street_address\": \"1st Street SE\", \"city\": \"Washington\", \"state\": \"DC\"} }", true); | |
278 | } | |
279 | ||
280 | TEST(SchemaValidator, Ref_AllOf) { | |
281 | Document sd; | |
282 | sd.Parse( | |
283 | "{" | |
284 | " \"$schema\": \"http://json-schema.org/draft-04/schema#\"," | |
285 | "" | |
286 | " \"definitions\": {" | |
287 | " \"address\": {" | |
288 | " \"type\": \"object\"," | |
289 | " \"properties\": {" | |
290 | " \"street_address\": { \"type\": \"string\" }," | |
291 | " \"city\": { \"type\": \"string\" }," | |
292 | " \"state\": { \"type\": \"string\" }" | |
293 | " }," | |
294 | " \"required\": [\"street_address\", \"city\", \"state\"]" | |
295 | " }" | |
296 | " }," | |
297 | " \"type\": \"object\"," | |
298 | " \"properties\": {" | |
299 | " \"billing_address\": { \"$ref\": \"#/definitions/address\" }," | |
300 | " \"shipping_address\": {" | |
301 | " \"allOf\": [" | |
302 | " { \"$ref\": \"#/definitions/address\" }," | |
303 | " { \"properties\":" | |
304 | " { \"type\": { \"enum\": [ \"residential\", \"business\" ] } }," | |
305 | " \"required\": [\"type\"]" | |
306 | " }" | |
307 | " ]" | |
308 | " }" | |
309 | " }" | |
310 | "}"); | |
311 | SchemaDocument s(sd); | |
312 | ||
313 | INVALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\"} }", "/properties/shipping_address", "allOf", "/shipping_address"); | |
314 | VALIDATE(s, "{\"shipping_address\": {\"street_address\": \"1600 Pennsylvania Avenue NW\", \"city\": \"Washington\", \"state\": \"DC\", \"type\": \"business\"} }", true); | |
315 | } | |
316 | ||
317 | TEST(SchemaValidator, String) { | |
318 | Document sd; | |
319 | sd.Parse("{\"type\":\"string\"}"); | |
320 | SchemaDocument s(sd); | |
321 | ||
322 | VALIDATE(s, "\"I'm a string\"", true); | |
323 | INVALIDATE(s, "42", "", "type", ""); | |
324 | INVALIDATE(s, "2147483648", "", "type", ""); // 2^31 can only be fit in unsigned | |
325 | INVALIDATE(s, "-2147483649", "", "type", ""); // -2^31 - 1 can only be fit in int64_t | |
326 | INVALIDATE(s, "4294967296", "", "type", ""); // 2^32 can only be fit in int64_t | |
327 | INVALIDATE(s, "3.1415926", "", "type", ""); | |
328 | } | |
329 | ||
330 | TEST(SchemaValidator, String_LengthRange) { | |
331 | Document sd; | |
332 | sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}"); | |
333 | SchemaDocument s(sd); | |
334 | ||
335 | INVALIDATE(s, "\"A\"", "", "minLength", ""); | |
336 | VALIDATE(s, "\"AB\"", true); | |
337 | VALIDATE(s, "\"ABC\"", true); | |
338 | INVALIDATE(s, "\"ABCD\"", "", "maxLength", ""); | |
339 | } | |
340 | ||
341 | #if RAPIDJSON_SCHEMA_HAS_REGEX | |
342 | TEST(SchemaValidator, String_Pattern) { | |
343 | Document sd; | |
344 | sd.Parse("{\"type\":\"string\",\"pattern\":\"^(\\\\([0-9]{3}\\\\))?[0-9]{3}-[0-9]{4}$\"}"); | |
345 | SchemaDocument s(sd); | |
346 | ||
347 | VALIDATE(s, "\"555-1212\"", true); | |
348 | VALIDATE(s, "\"(888)555-1212\"", true); | |
349 | INVALIDATE(s, "\"(888)555-1212 ext. 532\"", "", "pattern", ""); | |
350 | INVALIDATE(s, "\"(800)FLOWERS\"", "", "pattern", ""); | |
351 | } | |
352 | ||
353 | TEST(SchemaValidator, String_Pattern_Invalid) { | |
354 | Document sd; | |
355 | sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow | |
356 | SchemaDocument s(sd); | |
357 | ||
358 | VALIDATE(s, "\"\"", true); | |
359 | VALIDATE(s, "\"a\"", true); | |
360 | VALIDATE(s, "\"aa\"", true); | |
361 | } | |
362 | #endif | |
363 | ||
364 | TEST(SchemaValidator, Integer) { | |
365 | Document sd; | |
366 | sd.Parse("{\"type\":\"integer\"}"); | |
367 | SchemaDocument s(sd); | |
368 | ||
369 | VALIDATE(s, "42", true); | |
370 | VALIDATE(s, "-1", true); | |
371 | VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned | |
372 | VALIDATE(s, "-2147483649", true); // -2^31 - 1 can only be fit in int64_t | |
373 | VALIDATE(s, "2147483648", true); // 2^31 can only be fit in unsigned | |
374 | VALIDATE(s, "4294967296", true); // 2^32 can only be fit in int64_t | |
375 | INVALIDATE(s, "3.1415926", "", "type", ""); | |
376 | INVALIDATE(s, "\"42\"", "", "type", ""); | |
377 | } | |
378 | ||
379 | TEST(SchemaValidator, Integer_Range) { | |
380 | Document sd; | |
381 | sd.Parse("{\"type\":\"integer\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); | |
382 | SchemaDocument s(sd); | |
383 | ||
384 | INVALIDATE(s, "-1", "", "minimum", ""); | |
385 | VALIDATE(s, "0", true); | |
386 | VALIDATE(s, "10", true); | |
387 | VALIDATE(s, "99", true); | |
388 | INVALIDATE(s, "100", "", "maximum", ""); | |
389 | INVALIDATE(s, "101", "", "maximum", ""); | |
390 | } | |
391 | ||
392 | TEST(SchemaValidator, Integer_Range64Boundary) { | |
393 | Document sd; | |
394 | sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775807,\"maximum\":9223372036854775806}"); | |
395 | SchemaDocument s(sd); | |
396 | ||
397 | INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); | |
398 | VALIDATE(s, "-9223372036854775807", true); | |
399 | VALIDATE(s, "-2147483648", true); // int min | |
400 | VALIDATE(s, "0", true); | |
401 | VALIDATE(s, "2147483647", true); // int max | |
402 | VALIDATE(s, "2147483648", true); // unsigned first | |
403 | VALIDATE(s, "4294967295", true); // unsigned max | |
404 | VALIDATE(s, "9223372036854775806", true); | |
405 | INVALIDATE(s, "9223372036854775807", "", "maximum", ""); | |
406 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); // uint64_t max | |
407 | } | |
408 | ||
409 | TEST(SchemaValidator, Integer_RangeU64Boundary) { | |
410 | Document sd; | |
411 | sd.Parse("{\"type\":\"integer\",\"minimum\":9223372036854775808,\"maximum\":18446744073709551614}"); | |
412 | SchemaDocument s(sd); | |
413 | ||
414 | INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); | |
415 | INVALIDATE(s, "9223372036854775807", "", "minimum", ""); | |
416 | INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min | |
417 | INVALIDATE(s, "0", "", "minimum", ""); | |
418 | INVALIDATE(s, "2147483647", "", "minimum", ""); // int max | |
419 | INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first | |
420 | INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max | |
421 | VALIDATE(s, "9223372036854775808", true); | |
422 | VALIDATE(s, "18446744073709551614", true); | |
423 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
424 | } | |
425 | ||
426 | TEST(SchemaValidator, Integer_Range64BoundaryExclusive) { | |
427 | Document sd; | |
428 | sd.Parse("{\"type\":\"integer\",\"minimum\":-9223372036854775808,\"maximum\":18446744073709551615,\"exclusiveMinimum\":true,\"exclusiveMaximum\":true}"); | |
429 | SchemaDocument s(sd); | |
430 | ||
431 | INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); | |
432 | VALIDATE(s, "-9223372036854775807", true); | |
433 | VALIDATE(s, "18446744073709551614", true); | |
434 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
435 | } | |
436 | ||
437 | TEST(SchemaValidator, Integer_MultipleOf) { | |
438 | Document sd; | |
439 | sd.Parse("{\"type\":\"integer\",\"multipleOf\":10}"); | |
440 | SchemaDocument s(sd); | |
441 | ||
442 | VALIDATE(s, "0", true); | |
443 | VALIDATE(s, "10", true); | |
444 | VALIDATE(s, "-10", true); | |
445 | VALIDATE(s, "20", true); | |
446 | INVALIDATE(s, "23", "", "multipleOf", ""); | |
447 | INVALIDATE(s, "-23", "", "multipleOf", ""); | |
448 | } | |
449 | ||
450 | TEST(SchemaValidator, Integer_MultipleOf64Boundary) { | |
451 | Document sd; | |
452 | sd.Parse("{\"type\":\"integer\",\"multipleOf\":18446744073709551615}"); | |
453 | SchemaDocument s(sd); | |
454 | ||
455 | VALIDATE(s, "0", true); | |
456 | VALIDATE(s, "18446744073709551615", true); | |
457 | INVALIDATE(s, "18446744073709551614", "", "multipleOf", ""); | |
458 | } | |
459 | ||
460 | TEST(SchemaValidator, Number_Range) { | |
461 | Document sd; | |
462 | sd.Parse("{\"type\":\"number\",\"minimum\":0,\"maximum\":100,\"exclusiveMaximum\":true}"); | |
463 | SchemaDocument s(sd); | |
464 | ||
465 | INVALIDATE(s, "-1", "", "minimum", ""); | |
466 | VALIDATE(s, "0", true); | |
467 | VALIDATE(s, "0.1", true); | |
468 | VALIDATE(s, "10", true); | |
469 | VALIDATE(s, "99", true); | |
470 | VALIDATE(s, "99.9", true); | |
471 | INVALIDATE(s, "100", "", "maximum", ""); | |
472 | INVALIDATE(s, "100.0", "", "maximum", ""); | |
473 | INVALIDATE(s, "101.5", "", "maximum", ""); | |
474 | } | |
475 | ||
476 | TEST(SchemaValidator, Number_RangeInt) { | |
477 | Document sd; | |
478 | sd.Parse("{\"type\":\"number\",\"minimum\":-100,\"maximum\":-1,\"exclusiveMaximum\":true}"); | |
479 | SchemaDocument s(sd); | |
480 | ||
481 | INVALIDATE(s, "-101", "", "minimum", ""); | |
482 | INVALIDATE(s, "-100.1", "", "minimum", ""); | |
483 | VALIDATE(s, "-100", true); | |
484 | VALIDATE(s, "-2", true); | |
485 | INVALIDATE(s, "-1", "", "maximum", ""); | |
486 | INVALIDATE(s, "-0.9", "", "maximum", ""); | |
487 | INVALIDATE(s, "0", "", "maximum", ""); | |
488 | INVALIDATE(s, "2147483647", "", "maximum", ""); // int max | |
489 | INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first | |
490 | INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max | |
491 | INVALIDATE(s, "9223372036854775808", "", "maximum", ""); | |
492 | INVALIDATE(s, "18446744073709551614", "", "maximum", ""); | |
493 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
494 | } | |
495 | ||
496 | TEST(SchemaValidator, Number_RangeDouble) { | |
497 | Document sd; | |
498 | sd.Parse("{\"type\":\"number\",\"minimum\":0.1,\"maximum\":100.1,\"exclusiveMaximum\":true}"); | |
499 | SchemaDocument s(sd); | |
500 | ||
501 | INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); | |
502 | INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min | |
503 | INVALIDATE(s, "-1", "", "minimum", ""); | |
504 | VALIDATE(s, "0.1", true); | |
505 | VALIDATE(s, "10", true); | |
506 | VALIDATE(s, "99", true); | |
507 | VALIDATE(s, "100", true); | |
508 | INVALIDATE(s, "101", "", "maximum", ""); | |
509 | INVALIDATE(s, "101.5", "", "maximum", ""); | |
510 | INVALIDATE(s, "18446744073709551614", "", "maximum", ""); | |
511 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
512 | INVALIDATE(s, "2147483647", "", "maximum", ""); // int max | |
513 | INVALIDATE(s, "2147483648", "", "maximum", ""); // unsigned first | |
514 | INVALIDATE(s, "4294967295", "", "maximum", ""); // unsigned max | |
515 | INVALIDATE(s, "9223372036854775808", "", "maximum", ""); | |
516 | INVALIDATE(s, "18446744073709551614", "", "maximum", ""); | |
517 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
518 | } | |
519 | ||
520 | TEST(SchemaValidator, Number_RangeDoubleU64Boundary) { | |
521 | Document sd; | |
522 | sd.Parse("{\"type\":\"number\",\"minimum\":9223372036854775808.0,\"maximum\":18446744073709550000.0}"); | |
523 | SchemaDocument s(sd); | |
524 | ||
525 | INVALIDATE(s, "-9223372036854775808", "", "minimum", ""); | |
526 | INVALIDATE(s, "-2147483648", "", "minimum", ""); // int min | |
527 | INVALIDATE(s, "0", "", "minimum", ""); | |
528 | INVALIDATE(s, "2147483647", "", "minimum", ""); // int max | |
529 | INVALIDATE(s, "2147483648", "", "minimum", ""); // unsigned first | |
530 | INVALIDATE(s, "4294967295", "", "minimum", ""); // unsigned max | |
531 | VALIDATE(s, "9223372036854775808", true); | |
532 | VALIDATE(s, "18446744073709540000", true); | |
533 | INVALIDATE(s, "18446744073709551615", "", "maximum", ""); | |
534 | } | |
535 | ||
536 | TEST(SchemaValidator, Number_MultipleOf) { | |
537 | Document sd; | |
538 | sd.Parse("{\"type\":\"number\",\"multipleOf\":10.0}"); | |
539 | SchemaDocument s(sd); | |
540 | ||
541 | VALIDATE(s, "0", true); | |
542 | VALIDATE(s, "10", true); | |
543 | VALIDATE(s, "-10", true); | |
544 | VALIDATE(s, "20", true); | |
545 | INVALIDATE(s, "23", "", "multipleOf", ""); | |
546 | INVALIDATE(s, "-2147483648", "", "multipleOf", ""); // int min | |
547 | VALIDATE(s, "-2147483640", true); | |
548 | INVALIDATE(s, "2147483647", "", "multipleOf", ""); // int max | |
549 | INVALIDATE(s, "2147483648", "", "multipleOf", ""); // unsigned first | |
550 | VALIDATE(s, "2147483650", true); | |
551 | INVALIDATE(s, "4294967295", "", "multipleOf", ""); // unsigned max | |
552 | VALIDATE(s, "4294967300", true); | |
553 | } | |
554 | ||
555 | TEST(SchemaValidator, Number_MultipleOfOne) { | |
556 | Document sd; | |
557 | sd.Parse("{\"type\":\"number\",\"multipleOf\":1}"); | |
558 | SchemaDocument s(sd); | |
559 | ||
560 | VALIDATE(s, "42", true); | |
561 | VALIDATE(s, "42.0", true); | |
562 | INVALIDATE(s, "3.1415926", "", "multipleOf", ""); | |
563 | } | |
564 | ||
565 | TEST(SchemaValidator, Object) { | |
566 | Document sd; | |
567 | sd.Parse("{\"type\":\"object\"}"); | |
568 | SchemaDocument s(sd); | |
569 | ||
570 | VALIDATE(s, "{\"key\":\"value\",\"another_key\":\"another_value\"}", true); | |
571 | VALIDATE(s, "{\"Sun\":1.9891e30,\"Jupiter\":1.8986e27,\"Saturn\":5.6846e26,\"Neptune\":10.243e25,\"Uranus\":8.6810e25,\"Earth\":5.9736e24,\"Venus\":4.8685e24,\"Mars\":6.4185e23,\"Mercury\":3.3022e23,\"Moon\":7.349e22,\"Pluto\":1.25e22}", true); | |
572 | INVALIDATE(s, "[\"An\", \"array\", \"not\", \"an\", \"object\"]", "", "type", ""); | |
573 | INVALIDATE(s, "\"Not an object\"", "", "type", ""); | |
574 | } | |
575 | ||
576 | TEST(SchemaValidator, Object_Properties) { | |
577 | Document sd; | |
578 | sd.Parse( | |
579 | "{" | |
580 | " \"type\": \"object\"," | |
581 | " \"properties\" : {" | |
582 | " \"number\": { \"type\": \"number\" }," | |
583 | " \"street_name\" : { \"type\": \"string\" }," | |
584 | " \"street_type\" : { \"type\": \"string\", \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"] }" | |
585 | " }" | |
586 | "}"); | |
587 | ||
588 | SchemaDocument s(sd); | |
589 | ||
590 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); | |
591 | INVALIDATE(s, "{ \"number\": \"1600\", \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", "/properties/number", "type", "/number"); | |
592 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\" }", true); | |
593 | VALIDATE(s, "{}", true); | |
594 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); | |
595 | } | |
596 | ||
597 | TEST(SchemaValidator, Object_AdditionalPropertiesBoolean) { | |
598 | Document sd; | |
599 | sd.Parse( | |
600 | "{" | |
601 | " \"type\": \"object\"," | |
602 | " \"properties\" : {" | |
603 | " \"number\": { \"type\": \"number\" }," | |
604 | " \"street_name\" : { \"type\": \"string\" }," | |
605 | " \"street_type\" : { \"type\": \"string\"," | |
606 | " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]" | |
607 | " }" | |
608 | " }," | |
609 | " \"additionalProperties\": false" | |
610 | "}"); | |
611 | ||
612 | SchemaDocument s(sd); | |
613 | ||
614 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); | |
615 | INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", "", "additionalProperties", "/direction"); | |
616 | } | |
617 | ||
618 | TEST(SchemaValidator, Object_AdditionalPropertiesObject) { | |
619 | Document sd; | |
620 | sd.Parse( | |
621 | "{" | |
622 | " \"type\": \"object\"," | |
623 | " \"properties\" : {" | |
624 | " \"number\": { \"type\": \"number\" }," | |
625 | " \"street_name\" : { \"type\": \"string\" }," | |
626 | " \"street_type\" : { \"type\": \"string\"," | |
627 | " \"enum\" : [\"Street\", \"Avenue\", \"Boulevard\"]" | |
628 | " }" | |
629 | " }," | |
630 | " \"additionalProperties\": { \"type\": \"string\" }" | |
631 | "}"); | |
632 | SchemaDocument s(sd); | |
633 | ||
634 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\" }", true); | |
635 | VALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"direction\": \"NW\" }", true); | |
636 | INVALIDATE(s, "{ \"number\": 1600, \"street_name\": \"Pennsylvania\", \"street_type\": \"Avenue\", \"office_number\": 201 }", "/additionalProperties", "type", "/office_number"); | |
637 | } | |
638 | ||
639 | TEST(SchemaValidator, Object_Required) { | |
640 | Document sd; | |
641 | sd.Parse( | |
642 | "{" | |
643 | " \"type\": \"object\"," | |
644 | " \"properties\" : {" | |
645 | " \"name\": { \"type\": \"string\" }," | |
646 | " \"email\" : { \"type\": \"string\" }," | |
647 | " \"address\" : { \"type\": \"string\" }," | |
648 | " \"telephone\" : { \"type\": \"string\" }" | |
649 | " }," | |
650 | " \"required\":[\"name\", \"email\"]" | |
651 | "}"); | |
652 | SchemaDocument s(sd); | |
653 | ||
654 | VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\" }", true); | |
655 | VALIDATE(s, "{ \"name\": \"William Shakespeare\", \"email\" : \"bill@stratford-upon-avon.co.uk\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\", \"authorship\" : \"in question\"}", true); | |
656 | INVALIDATE(s, "{ \"name\": \"William Shakespeare\", \"address\" : \"Henley Street, Stratford-upon-Avon, Warwickshire, England\" }", "", "required", ""); | |
657 | } | |
658 | ||
659 | ||
660 | TEST(SchemaValidator, Object_PropertiesRange) { | |
661 | Document sd; | |
662 | sd.Parse("{\"type\":\"object\", \"minProperties\":2, \"maxProperties\":3}"); | |
663 | SchemaDocument s(sd); | |
664 | ||
665 | INVALIDATE(s, "{}", "", "minProperties", ""); | |
666 | INVALIDATE(s, "{\"a\":0}", "", "minProperties", ""); | |
667 | VALIDATE(s, "{\"a\":0,\"b\":1}", true); | |
668 | VALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2}", true); | |
669 | INVALIDATE(s, "{\"a\":0,\"b\":1,\"c\":2,\"d\":3}", "", "maxProperties", ""); | |
670 | } | |
671 | ||
672 | TEST(SchemaValidator, Object_PropertyDependencies) { | |
673 | Document sd; | |
674 | sd.Parse( | |
675 | "{" | |
676 | " \"type\": \"object\"," | |
677 | " \"properties\": {" | |
678 | " \"name\": { \"type\": \"string\" }," | |
679 | " \"credit_card\": { \"type\": \"number\" }," | |
680 | " \"billing_address\": { \"type\": \"string\" }" | |
681 | " }," | |
682 | " \"required\": [\"name\"]," | |
683 | " \"dependencies\": {" | |
684 | " \"credit_card\": [\"billing_address\"]" | |
685 | " }" | |
686 | "}"); | |
687 | SchemaDocument s(sd); | |
688 | ||
689 | VALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555, \"billing_address\": \"555 Debtor's Lane\" }", true); | |
690 | INVALIDATE(s, "{ \"name\": \"John Doe\", \"credit_card\": 5555555555555555 }", "", "dependencies", ""); | |
691 | VALIDATE(s, "{ \"name\": \"John Doe\"}", true); | |
692 | VALIDATE(s, "{ \"name\": \"John Doe\", \"billing_address\": \"555 Debtor's Lane\" }", true); | |
693 | } | |
694 | ||
695 | TEST(SchemaValidator, Object_SchemaDependencies) { | |
696 | Document sd; | |
697 | sd.Parse( | |
698 | "{" | |
699 | " \"type\": \"object\"," | |
700 | " \"properties\" : {" | |
701 | " \"name\": { \"type\": \"string\" }," | |
702 | " \"credit_card\" : { \"type\": \"number\" }" | |
703 | " }," | |
704 | " \"required\" : [\"name\"]," | |
705 | " \"dependencies\" : {" | |
706 | " \"credit_card\": {" | |
707 | " \"properties\": {" | |
708 | " \"billing_address\": { \"type\": \"string\" }" | |
709 | " }," | |
710 | " \"required\" : [\"billing_address\"]" | |
711 | " }" | |
712 | " }" | |
713 | "}"); | |
714 | SchemaDocument s(sd); | |
715 | ||
716 | VALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555,\"billing_address\" : \"555 Debtor's Lane\"}", true); | |
717 | INVALIDATE(s, "{\"name\": \"John Doe\", \"credit_card\" : 5555555555555555 }", "", "dependencies", ""); | |
718 | VALIDATE(s, "{\"name\": \"John Doe\", \"billing_address\" : \"555 Debtor's Lane\"}", true); | |
719 | } | |
720 | ||
721 | #if RAPIDJSON_SCHEMA_HAS_REGEX | |
722 | TEST(SchemaValidator, Object_PatternProperties) { | |
723 | Document sd; | |
724 | sd.Parse( | |
725 | "{" | |
726 | " \"type\": \"object\"," | |
727 | " \"patternProperties\": {" | |
728 | " \"^S_\": { \"type\": \"string\" }," | |
729 | " \"^I_\": { \"type\": \"integer\" }" | |
730 | " }" | |
731 | "}"); | |
732 | SchemaDocument s(sd); | |
733 | ||
734 | VALIDATE(s, "{ \"S_25\": \"This is a string\" }", true); | |
735 | VALIDATE(s, "{ \"I_0\": 42 }", true); | |
736 | INVALIDATE(s, "{ \"S_0\": 42 }", "", "patternProperties", "/S_0"); | |
737 | INVALIDATE(s, "{ \"I_42\": \"This is a string\" }", "", "patternProperties", "/I_42"); | |
738 | VALIDATE(s, "{ \"keyword\": \"value\" }", true); | |
739 | } | |
740 | ||
741 | TEST(SchemaValidator, Object_PatternProperties_AdditionalProperties) { | |
742 | Document sd; | |
743 | sd.Parse( | |
744 | "{" | |
745 | " \"type\": \"object\"," | |
746 | " \"properties\": {" | |
747 | " \"builtin\": { \"type\": \"number\" }" | |
748 | " }," | |
749 | " \"patternProperties\": {" | |
750 | " \"^S_\": { \"type\": \"string\" }," | |
751 | " \"^I_\": { \"type\": \"integer\" }" | |
752 | " }," | |
753 | " \"additionalProperties\": { \"type\": \"string\" }" | |
754 | "}"); | |
755 | SchemaDocument s(sd); | |
756 | ||
757 | VALIDATE(s, "{ \"builtin\": 42 }", true); | |
758 | VALIDATE(s, "{ \"keyword\": \"value\" }", true); | |
759 | INVALIDATE(s, "{ \"keyword\": 42 }", "/additionalProperties", "type", "/keyword"); | |
760 | } | |
761 | #endif | |
762 | ||
763 | TEST(SchemaValidator, Array) { | |
764 | Document sd; | |
765 | sd.Parse("{\"type\":\"array\"}"); | |
766 | SchemaDocument s(sd); | |
767 | ||
768 | VALIDATE(s, "[1, 2, 3, 4, 5]", true); | |
769 | VALIDATE(s, "[3, \"different\", { \"types\" : \"of values\" }]", true); | |
770 | INVALIDATE(s, "{\"Not\": \"an array\"}", "", "type", ""); | |
771 | } | |
772 | ||
773 | TEST(SchemaValidator, Array_ItemsList) { | |
774 | Document sd; | |
775 | sd.Parse( | |
776 | "{" | |
777 | " \"type\": \"array\"," | |
778 | " \"items\" : {" | |
779 | " \"type\": \"number\"" | |
780 | " }" | |
781 | "}"); | |
782 | SchemaDocument s(sd); | |
783 | ||
784 | VALIDATE(s, "[1, 2, 3, 4, 5]", true); | |
785 | INVALIDATE(s, "[1, 2, \"3\", 4, 5]", "/items", "type", "/2"); | |
786 | VALIDATE(s, "[]", true); | |
787 | } | |
788 | ||
789 | TEST(SchemaValidator, Array_ItemsTuple) { | |
790 | Document sd; | |
791 | sd.Parse( | |
792 | "{" | |
793 | " \"type\": \"array\"," | |
794 | " \"items\": [" | |
795 | " {" | |
796 | " \"type\": \"number\"" | |
797 | " }," | |
798 | " {" | |
799 | " \"type\": \"string\"" | |
800 | " }," | |
801 | " {" | |
802 | " \"type\": \"string\"," | |
803 | " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]" | |
804 | " }," | |
805 | " {" | |
806 | " \"type\": \"string\"," | |
807 | " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]" | |
808 | " }" | |
809 | " ]" | |
810 | "}"); | |
811 | SchemaDocument s(sd); | |
812 | ||
813 | VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); | |
814 | INVALIDATE(s, "[24, \"Sussex\", \"Drive\"]", "/items/2", "enum", "/2"); | |
815 | INVALIDATE(s, "[\"Palais de l'Elysee\"]", "/items/0", "type", "/0"); | |
816 | VALIDATE(s, "[10, \"Downing\", \"Street\"]", true); | |
817 | VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", true); | |
818 | } | |
819 | ||
820 | TEST(SchemaValidator, Array_AdditionalItmes) { | |
821 | Document sd; | |
822 | sd.Parse( | |
823 | "{" | |
824 | " \"type\": \"array\"," | |
825 | " \"items\": [" | |
826 | " {" | |
827 | " \"type\": \"number\"" | |
828 | " }," | |
829 | " {" | |
830 | " \"type\": \"string\"" | |
831 | " }," | |
832 | " {" | |
833 | " \"type\": \"string\"," | |
834 | " \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"]" | |
835 | " }," | |
836 | " {" | |
837 | " \"type\": \"string\"," | |
838 | " \"enum\": [\"NW\", \"NE\", \"SW\", \"SE\"]" | |
839 | " }" | |
840 | " ]," | |
841 | " \"additionalItems\": false" | |
842 | "}"); | |
843 | SchemaDocument s(sd); | |
844 | ||
845 | VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\"]", true); | |
846 | VALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\"]", true); | |
847 | INVALIDATE(s, "[1600, \"Pennsylvania\", \"Avenue\", \"NW\", \"Washington\"]", "", "items", "/4"); | |
848 | } | |
849 | ||
850 | TEST(SchemaValidator, Array_ItemsRange) { | |
851 | Document sd; | |
852 | sd.Parse("{\"type\": \"array\",\"minItems\": 2,\"maxItems\" : 3}"); | |
853 | SchemaDocument s(sd); | |
854 | ||
855 | INVALIDATE(s, "[]", "", "minItems", ""); | |
856 | INVALIDATE(s, "[1]", "", "minItems", ""); | |
857 | VALIDATE(s, "[1, 2]", true); | |
858 | VALIDATE(s, "[1, 2, 3]", true); | |
859 | INVALIDATE(s, "[1, 2, 3, 4]", "", "maxItems", ""); | |
860 | } | |
861 | ||
862 | TEST(SchemaValidator, Array_UniqueItems) { | |
863 | Document sd; | |
864 | sd.Parse("{\"type\": \"array\", \"uniqueItems\": true}"); | |
865 | SchemaDocument s(sd); | |
866 | ||
867 | VALIDATE(s, "[1, 2, 3, 4, 5]", true); | |
868 | INVALIDATE(s, "[1, 2, 3, 3, 4]", "", "uniqueItems", "/3"); | |
869 | VALIDATE(s, "[]", true); | |
870 | } | |
871 | ||
872 | TEST(SchemaValidator, Boolean) { | |
873 | Document sd; | |
874 | sd.Parse("{\"type\":\"boolean\"}"); | |
875 | SchemaDocument s(sd); | |
876 | ||
877 | VALIDATE(s, "true", true); | |
878 | VALIDATE(s, "false", true); | |
879 | INVALIDATE(s, "\"true\"", "", "type", ""); | |
880 | INVALIDATE(s, "0", "", "type", ""); | |
881 | } | |
882 | ||
883 | TEST(SchemaValidator, Null) { | |
884 | Document sd; | |
885 | sd.Parse("{\"type\":\"null\"}"); | |
886 | SchemaDocument s(sd); | |
887 | ||
888 | VALIDATE(s, "null", true); | |
889 | INVALIDATE(s, "false", "", "type", ""); | |
890 | INVALIDATE(s, "0", "", "type", ""); | |
891 | INVALIDATE(s, "\"\"", "", "type", ""); | |
892 | } | |
893 | ||
894 | // Additional tests | |
895 | ||
896 | TEST(SchemaValidator, ObjectInArray) { | |
897 | Document sd; | |
898 | sd.Parse("{\"type\":\"array\", \"items\": { \"type\":\"string\" }}"); | |
899 | SchemaDocument s(sd); | |
900 | ||
901 | VALIDATE(s, "[\"a\"]", true); | |
902 | INVALIDATE(s, "[1]", "/items", "type", "/0"); | |
903 | INVALIDATE(s, "[{}]", "/items", "type", "/0"); | |
904 | } | |
905 | ||
906 | TEST(SchemaValidator, MultiTypeInObject) { | |
907 | Document sd; | |
908 | sd.Parse( | |
909 | "{" | |
910 | " \"type\":\"object\"," | |
911 | " \"properties\": {" | |
912 | " \"tel\" : {" | |
913 | " \"type\":[\"integer\", \"string\"]" | |
914 | " }" | |
915 | " }" | |
916 | "}"); | |
917 | SchemaDocument s(sd); | |
918 | ||
919 | VALIDATE(s, "{ \"tel\": 999 }", true); | |
920 | VALIDATE(s, "{ \"tel\": \"123-456\" }", true); | |
921 | INVALIDATE(s, "{ \"tel\": true }", "/properties/tel", "type", "/tel"); | |
922 | } | |
923 | ||
924 | TEST(SchemaValidator, MultiTypeWithObject) { | |
925 | Document sd; | |
926 | sd.Parse( | |
927 | "{" | |
928 | " \"type\": [\"object\",\"string\"]," | |
929 | " \"properties\": {" | |
930 | " \"tel\" : {" | |
931 | " \"type\": \"integer\"" | |
932 | " }" | |
933 | " }" | |
934 | "}"); | |
935 | SchemaDocument s(sd); | |
936 | ||
937 | VALIDATE(s, "\"Hello\"", true); | |
938 | VALIDATE(s, "{ \"tel\": 999 }", true); | |
939 | INVALIDATE(s, "{ \"tel\": \"fail\" }", "/properties/tel", "type", "/tel"); | |
940 | } | |
941 | ||
942 | TEST(SchemaValidator, AllOf_Nested) { | |
943 | Document sd; | |
944 | sd.Parse( | |
945 | "{" | |
946 | " \"allOf\": [" | |
947 | " { \"type\": \"string\", \"minLength\": 2 }," | |
948 | " { \"type\": \"string\", \"maxLength\": 5 }," | |
949 | " { \"allOf\": [ { \"enum\" : [\"ok\", \"okay\", \"OK\", \"o\"] }, { \"enum\" : [\"ok\", \"OK\", \"o\"]} ] }" | |
950 | " ]" | |
951 | "}"); | |
952 | SchemaDocument s(sd); | |
953 | ||
954 | VALIDATE(s, "\"ok\"", true); | |
955 | VALIDATE(s, "\"OK\"", true); | |
956 | INVALIDATE(s, "\"okay\"", "", "allOf", ""); | |
957 | INVALIDATE(s, "\"o\"", "", "allOf", ""); | |
958 | INVALIDATE(s, "\"n\"", "", "allOf", ""); | |
959 | INVALIDATE(s, "\"too long\"", "", "allOf", ""); | |
960 | INVALIDATE(s, "123", "", "allOf", ""); | |
961 | } | |
962 | ||
963 | TEST(SchemaValidator, EscapedPointer) { | |
964 | Document sd; | |
965 | sd.Parse( | |
966 | "{" | |
967 | " \"type\": \"object\"," | |
968 | " \"properties\": {" | |
969 | " \"~/\": { \"type\": \"number\" }" | |
970 | " }" | |
971 | "}"); | |
972 | SchemaDocument s(sd); | |
973 | INVALIDATE(s, "{\"~/\":true}", "/properties/~0~1", "type", "/~0~1"); | |
974 | } | |
975 | ||
976 | template <typename Allocator> | |
977 | static char* ReadFile(const char* filename, Allocator& allocator) { | |
978 | const char *paths[] = { | |
979 | "", | |
980 | "bin/", | |
981 | "../bin/", | |
982 | "../../bin/", | |
983 | "../../../bin/" | |
984 | }; | |
985 | char buffer[1024]; | |
986 | FILE *fp = 0; | |
987 | for (size_t i = 0; i < sizeof(paths) / sizeof(paths[0]); i++) { | |
988 | sprintf(buffer, "%s%s", paths[i], filename); | |
989 | fp = fopen(buffer, "rb"); | |
990 | if (fp) | |
991 | break; | |
992 | } | |
993 | ||
994 | if (!fp) | |
995 | return 0; | |
996 | ||
997 | fseek(fp, 0, SEEK_END); | |
998 | size_t length = static_cast<size_t>(ftell(fp)); | |
999 | fseek(fp, 0, SEEK_SET); | |
1000 | char* json = reinterpret_cast<char*>(allocator.Malloc(length + 1)); | |
1001 | size_t readLength = fread(json, 1, length, fp); | |
1002 | json[readLength] = '\0'; | |
1003 | fclose(fp); | |
1004 | return json; | |
1005 | } | |
1006 | ||
1007 | TEST(SchemaValidator, ValidateMetaSchema) { | |
1008 | CrtAllocator allocator; | |
1009 | char* json = ReadFile("draft-04/schema", allocator); | |
1010 | Document d; | |
1011 | d.Parse(json); | |
1012 | ASSERT_FALSE(d.HasParseError()); | |
1013 | SchemaDocument sd(d); | |
1014 | SchemaValidator validator(sd); | |
1015 | if (!d.Accept(validator)) { | |
1016 | StringBuffer sb; | |
1017 | validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); | |
1018 | printf("Invalid schema: %s\n", sb.GetString()); | |
1019 | printf("Invalid keyword: %s\n", validator.GetInvalidSchemaKeyword()); | |
1020 | sb.Clear(); | |
1021 | validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); | |
1022 | printf("Invalid document: %s\n", sb.GetString()); | |
1023 | ADD_FAILURE(); | |
1024 | } | |
1025 | CrtAllocator::Free(json); | |
1026 | } | |
1027 | ||
1028 | TEST(SchemaValidator, ValidateMetaSchema_UTF16) { | |
1029 | typedef GenericDocument<UTF16<> > D; | |
1030 | typedef GenericSchemaDocument<D::ValueType> SD; | |
1031 | typedef GenericSchemaValidator<SD> SV; | |
1032 | ||
1033 | CrtAllocator allocator; | |
1034 | char* json = ReadFile("draft-04/schema", allocator); | |
1035 | ||
1036 | D d; | |
1037 | StringStream ss(json); | |
1038 | d.ParseStream<0, UTF8<> >(ss); | |
1039 | ASSERT_FALSE(d.HasParseError()); | |
1040 | SD sd(d); | |
1041 | SV validator(sd); | |
1042 | if (!d.Accept(validator)) { | |
1043 | GenericStringBuffer<UTF16<> > sb; | |
1044 | validator.GetInvalidSchemaPointer().StringifyUriFragment(sb); | |
1045 | wprintf(L"Invalid schema: %ls\n", sb.GetString()); | |
1046 | wprintf(L"Invalid keyword: %ls\n", validator.GetInvalidSchemaKeyword()); | |
1047 | sb.Clear(); | |
1048 | validator.GetInvalidDocumentPointer().StringifyUriFragment(sb); | |
1049 | wprintf(L"Invalid document: %ls\n", sb.GetString()); | |
1050 | ADD_FAILURE(); | |
1051 | } | |
1052 | CrtAllocator::Free(json); | |
1053 | } | |
1054 | ||
1055 | template <typename SchemaDocumentType = SchemaDocument> | |
1056 | class RemoteSchemaDocumentProvider : public IGenericRemoteSchemaDocumentProvider<SchemaDocumentType> { | |
1057 | public: | |
1058 | RemoteSchemaDocumentProvider() : | |
1059 | documentAllocator_(documentBuffer_, sizeof(documentBuffer_)), | |
1060 | schemaAllocator_(schemaBuffer_, sizeof(schemaBuffer_)) | |
1061 | { | |
1062 | const char* filenames[kCount] = { | |
1063 | "jsonschema/remotes/integer.json", | |
1064 | "jsonschema/remotes/subSchemas.json", | |
1065 | "jsonschema/remotes/folder/folderInteger.json", | |
1066 | "draft-04/schema" | |
1067 | }; | |
1068 | ||
1069 | for (size_t i = 0; i < kCount; i++) { | |
1070 | sd_[i] = 0; | |
1071 | ||
1072 | char jsonBuffer[8192]; | |
1073 | MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer)); | |
1074 | char* json = ReadFile(filenames[i], jsonAllocator); | |
1075 | if (!json) { | |
1076 | printf("json remote file %s not found", filenames[i]); | |
1077 | ADD_FAILURE(); | |
1078 | } | |
1079 | else { | |
1080 | char stackBuffer[4096]; | |
1081 | MemoryPoolAllocator<> stackAllocator(stackBuffer, sizeof(stackBuffer)); | |
1082 | DocumentType d(&documentAllocator_, 1024, &stackAllocator); | |
1083 | d.Parse(json); | |
1084 | sd_[i] = new SchemaDocumentType(d, 0, &schemaAllocator_); | |
1085 | MemoryPoolAllocator<>::Free(json); | |
1086 | } | |
1087 | }; | |
1088 | } | |
1089 | ||
1090 | ~RemoteSchemaDocumentProvider() { | |
1091 | for (size_t i = 0; i < kCount; i++) | |
1092 | delete sd_[i]; | |
1093 | } | |
1094 | ||
1095 | virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) { | |
1096 | const char* uris[kCount] = { | |
1097 | "http://localhost:1234/integer.json", | |
1098 | "http://localhost:1234/subSchemas.json", | |
1099 | "http://localhost:1234/folder/folderInteger.json", | |
1100 | "http://json-schema.org/draft-04/schema" | |
1101 | }; | |
1102 | ||
1103 | for (size_t i = 0; i < kCount; i++) | |
1104 | if (strncmp(uri, uris[i], length) == 0) | |
1105 | return sd_[i]; | |
1106 | return 0; | |
1107 | } | |
1108 | ||
1109 | private: | |
1110 | typedef GenericDocument<typename SchemaDocumentType::EncodingType, MemoryPoolAllocator<>, MemoryPoolAllocator<> > DocumentType; | |
1111 | ||
1112 | RemoteSchemaDocumentProvider(const RemoteSchemaDocumentProvider&); | |
1113 | RemoteSchemaDocumentProvider& operator=(const RemoteSchemaDocumentProvider&); | |
1114 | ||
1115 | static const size_t kCount = 4; | |
1116 | SchemaDocumentType* sd_[kCount]; | |
1117 | typename DocumentType::AllocatorType documentAllocator_; | |
1118 | typename SchemaDocumentType::AllocatorType schemaAllocator_; | |
1119 | char documentBuffer_[16384]; | |
1120 | char schemaBuffer_[128 * 1024]; | |
1121 | }; | |
1122 | ||
1123 | TEST(SchemaValidator, TestSuite) { | |
1124 | const char* filenames[] = { | |
1125 | "additionalItems.json", | |
1126 | "additionalProperties.json", | |
1127 | "allOf.json", | |
1128 | "anyOf.json", | |
1129 | "default.json", | |
1130 | "definitions.json", | |
1131 | "dependencies.json", | |
1132 | "enum.json", | |
1133 | "items.json", | |
1134 | "maximum.json", | |
1135 | "maxItems.json", | |
1136 | "maxLength.json", | |
1137 | "maxProperties.json", | |
1138 | "minimum.json", | |
1139 | "minItems.json", | |
1140 | "minLength.json", | |
1141 | "minProperties.json", | |
1142 | "multipleOf.json", | |
1143 | "not.json", | |
1144 | "oneOf.json", | |
1145 | "pattern.json", | |
1146 | "patternProperties.json", | |
1147 | "properties.json", | |
1148 | "ref.json", | |
1149 | "refRemote.json", | |
1150 | "required.json", | |
1151 | "type.json", | |
1152 | "uniqueItems.json" | |
1153 | }; | |
1154 | ||
1155 | const char* onlyRunDescription = 0; | |
1156 | //const char* onlyRunDescription = "a string is a string"; | |
1157 | ||
1158 | unsigned testCount = 0; | |
1159 | unsigned passCount = 0; | |
1160 | ||
1161 | typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType; | |
1162 | RemoteSchemaDocumentProvider<SchemaDocumentType> provider; | |
1163 | ||
1164 | char jsonBuffer[65536]; | |
1165 | char documentBuffer[65536]; | |
1166 | char documentStackBuffer[65536]; | |
1167 | char schemaBuffer[65536]; | |
1168 | char validatorBuffer[65536]; | |
1169 | MemoryPoolAllocator<> jsonAllocator(jsonBuffer, sizeof(jsonBuffer)); | |
1170 | MemoryPoolAllocator<> documentAllocator(documentBuffer, sizeof(documentBuffer)); | |
1171 | MemoryPoolAllocator<> documentStackAllocator(documentStackBuffer, sizeof(documentStackBuffer)); | |
1172 | MemoryPoolAllocator<> schemaAllocator(schemaBuffer, sizeof(schemaBuffer)); | |
1173 | MemoryPoolAllocator<> validatorAllocator(validatorBuffer, sizeof(validatorBuffer)); | |
1174 | ||
1175 | for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) { | |
1176 | char filename[FILENAME_MAX]; | |
1177 | sprintf(filename, "jsonschema/tests/draft4/%s", filenames[i]); | |
1178 | char* json = ReadFile(filename, jsonAllocator); | |
1179 | if (!json) { | |
1180 | printf("json test suite file %s not found", filename); | |
1181 | ADD_FAILURE(); | |
1182 | } | |
1183 | else { | |
1184 | GenericDocument<UTF8<>, MemoryPoolAllocator<>, MemoryPoolAllocator<> > d(&documentAllocator, 1024, &documentStackAllocator); | |
1185 | d.Parse(json); | |
1186 | if (d.HasParseError()) { | |
1187 | printf("json test suite file %s has parse error", filename); | |
1188 | ADD_FAILURE(); | |
1189 | } | |
1190 | else { | |
1191 | for (Value::ConstValueIterator schemaItr = d.Begin(); schemaItr != d.End(); ++schemaItr) { | |
1192 | { | |
1193 | SchemaDocumentType schema((*schemaItr)["schema"], &provider, &schemaAllocator); | |
1194 | GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > validator(schema, &validatorAllocator); | |
1195 | const char* description1 = (*schemaItr)["description"].GetString(); | |
1196 | const Value& tests = (*schemaItr)["tests"]; | |
1197 | for (Value::ConstValueIterator testItr = tests.Begin(); testItr != tests.End(); ++testItr) { | |
1198 | const char* description2 = (*testItr)["description"].GetString(); | |
1199 | if (!onlyRunDescription || strcmp(description2, onlyRunDescription) == 0) { | |
1200 | const Value& data = (*testItr)["data"]; | |
1201 | bool expected = (*testItr)["valid"].GetBool(); | |
1202 | testCount++; | |
1203 | validator.Reset(); | |
1204 | bool actual = data.Accept(validator); | |
1205 | if (expected != actual) | |
1206 | printf("Fail: %30s \"%s\" \"%s\"\n", filename, description1, description2); | |
1207 | else | |
1208 | passCount++; | |
1209 | } | |
1210 | } | |
1211 | //printf("%zu %zu %zu\n", documentAllocator.Size(), schemaAllocator.Size(), validatorAllocator.Size()); | |
1212 | } | |
1213 | schemaAllocator.Clear(); | |
1214 | validatorAllocator.Clear(); | |
1215 | } | |
1216 | } | |
1217 | } | |
1218 | documentAllocator.Clear(); | |
1219 | MemoryPoolAllocator<>::Free(json); | |
1220 | jsonAllocator.Clear(); | |
1221 | } | |
1222 | printf("%d / %d passed (%2d%%)\n", passCount, testCount, passCount * 100 / testCount); | |
1223 | // if (passCount != testCount) | |
1224 | // ADD_FAILURE(); | |
1225 | } | |
1226 | ||
1227 | TEST(SchemaValidatingReader, Simple) { | |
1228 | Document sd; | |
1229 | sd.Parse("{ \"type\": \"string\", \"enum\" : [\"red\", \"amber\", \"green\"] }"); | |
1230 | SchemaDocument s(sd); | |
1231 | ||
1232 | Document d; | |
1233 | StringStream ss("\"red\""); | |
1234 | SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s); | |
1235 | d.Populate(reader); | |
1236 | EXPECT_TRUE(reader.GetParseResult()); | |
1237 | EXPECT_TRUE(reader.IsValid()); | |
1238 | EXPECT_TRUE(d.IsString()); | |
1239 | EXPECT_STREQ("red", d.GetString()); | |
1240 | } | |
1241 | ||
1242 | TEST(SchemaValidatingReader, Invalid) { | |
1243 | Document sd; | |
1244 | sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}"); | |
1245 | SchemaDocument s(sd); | |
1246 | ||
1247 | Document d; | |
1248 | StringStream ss("\"ABCD\""); | |
1249 | SchemaValidatingReader<kParseDefaultFlags, StringStream, UTF8<> > reader(ss, s); | |
1250 | d.Populate(reader); | |
1251 | EXPECT_FALSE(reader.GetParseResult()); | |
1252 | EXPECT_FALSE(reader.IsValid()); | |
1253 | EXPECT_EQ(kParseErrorTermination, reader.GetParseResult().Code()); | |
1254 | EXPECT_STREQ("maxLength", reader.GetInvalidSchemaKeyword()); | |
1255 | EXPECT_TRUE(reader.GetInvalidSchemaPointer() == SchemaDocument::PointerType("")); | |
1256 | EXPECT_TRUE(reader.GetInvalidDocumentPointer() == SchemaDocument::PointerType("")); | |
1257 | EXPECT_TRUE(d.IsNull()); | |
1258 | } | |
1259 | ||
1260 | TEST(SchemaValidatingWriter, Simple) { | |
1261 | Document sd; | |
1262 | sd.Parse("{\"type\":\"string\",\"minLength\":2,\"maxLength\":3}"); | |
1263 | SchemaDocument s(sd); | |
1264 | ||
1265 | Document d; | |
1266 | StringBuffer sb; | |
1267 | Writer<StringBuffer> writer(sb); | |
1268 | GenericSchemaValidator<SchemaDocument, Writer<StringBuffer> > validator(s, writer); | |
1269 | ||
1270 | d.Parse("\"red\""); | |
1271 | EXPECT_TRUE(d.Accept(validator)); | |
1272 | EXPECT_TRUE(validator.IsValid()); | |
1273 | EXPECT_STREQ("\"red\"", sb.GetString()); | |
1274 | ||
1275 | sb.Clear(); | |
1276 | validator.Reset(); | |
1277 | d.Parse("\"ABCD\""); | |
1278 | EXPECT_FALSE(d.Accept(validator)); | |
1279 | EXPECT_FALSE(validator.IsValid()); | |
1280 | EXPECT_TRUE(validator.GetInvalidSchemaPointer() == SchemaDocument::PointerType("")); | |
1281 | EXPECT_TRUE(validator.GetInvalidDocumentPointer() == SchemaDocument::PointerType("")); | |
1282 | } | |
1283 | ||
1284 | #if RAPIDJSON_HAS_CXX11_RVALUE_REFS | |
1285 | ||
1286 | static SchemaDocument ReturnSchemaDocument() { | |
1287 | Document sd; | |
1288 | sd.Parse("{ \"type\": [\"number\", \"string\"] }"); | |
1289 | SchemaDocument s(sd); | |
1290 | return s; | |
1291 | } | |
1292 | ||
1293 | TEST(Schema, Issue552) { | |
1294 | SchemaDocument s = ReturnSchemaDocument(); | |
1295 | VALIDATE(s, "42", true); | |
1296 | VALIDATE(s, "\"Life, the universe, and everything\"", true); | |
1297 | INVALIDATE(s, "[\"Life\", \"the universe\", \"and everything\"]", "", "type", ""); | |
1298 | } | |
1299 | ||
1300 | #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS | |
1301 | ||
1302 | TEST(SchemaValidator, Issue608) { | |
1303 | Document sd; | |
1304 | sd.Parse("{\"required\": [\"a\", \"b\"] }"); | |
1305 | SchemaDocument s(sd); | |
1306 | ||
1307 | VALIDATE(s, "{\"a\" : null, \"b\": null}", true); | |
1308 | INVALIDATE(s, "{\"a\" : null, \"a\" : null}", "", "required", ""); | |
1309 | } | |
1310 | ||
1311 | #ifdef __clang__ | |
1312 | RAPIDJSON_DIAG_POP | |
1313 | #endif |