schematest.cpp 47 KB

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