SchemaTest.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. package org.leumasjaffe.json.schema;
  2. import static com.fasterxml.jackson.databind.node.JsonNodeType.*;
  3. import static org.junit.Assert.assertThat;
  4. import static org.leumasjaffe.json.schema.matcher.Accepts.accepts;
  5. import static org.leumasjaffe.json.schema.matcher.Not.not;
  6. import java.util.Arrays;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. import org.junit.Test;
  10. import org.leumasjaffe.json.schema.tester.AllItemsTester;
  11. import org.leumasjaffe.json.schema.tester.ContainsTester;
  12. import org.leumasjaffe.json.schema.tester.EqualsTester;
  13. import org.leumasjaffe.json.schema.tester.FixedTester;
  14. import org.leumasjaffe.json.schema.tester.FormatTester;
  15. import org.leumasjaffe.json.schema.tester.ItemsTester;
  16. import org.leumasjaffe.json.schema.tester.NumberTester;
  17. import org.leumasjaffe.json.schema.tester.PropertyNameTester;
  18. import org.leumasjaffe.json.schema.tester.PropertyTester;
  19. import org.leumasjaffe.json.schema.tester.SizeTester;
  20. import org.leumasjaffe.json.schema.tester.TypeTester;
  21. import org.leumasjaffe.json.schema.tester.UniqueItemTester;
  22. import com.fasterxml.jackson.databind.JsonNode;
  23. import com.fasterxml.jackson.databind.node.ArrayNode;
  24. import com.fasterxml.jackson.databind.node.BooleanNode;
  25. import com.fasterxml.jackson.databind.node.DoubleNode;
  26. import com.fasterxml.jackson.databind.node.JsonNodeFactory;
  27. import com.fasterxml.jackson.databind.node.NullNode;
  28. import com.fasterxml.jackson.databind.node.ObjectNode;
  29. import com.fasterxml.jackson.databind.node.TextNode;
  30. // TODO: patternProperties
  31. // TODO: dependencies
  32. // TODO: $ref
  33. // TODO: additionalItems with mixed results
  34. public class SchemaTest {
  35. private Schema getConstSchema(JsonNode json) {
  36. Map<String, Tester> tests = new HashMap<>();
  37. tests.put("const", new EqualsTester(json));
  38. return new Schema(tests);
  39. }
  40. private Schema getEnumSchema(JsonNode... jsons) {
  41. Map<String, Tester> tests = new HashMap<>();
  42. tests.put("enum", new EqualsTester(jsons));
  43. return new Schema(tests);
  44. }
  45. private Schema getNumberSchema() {
  46. Map<String, Tester> tests = new HashMap<>();
  47. tests.put("type", TypeTester.fromType("number"));
  48. tests.put("minimum", new NumberTester(d -> d >= 0.0));
  49. tests.put("multipleOf", new NumberTester(d -> Math.abs(d % 0.25) < 1E-7));
  50. tests.put("exclusiveMaximum", new NumberTester(d -> d < 1.0));
  51. return new Schema(tests);
  52. }
  53. private Schema getStringSchema() {
  54. Map<String, Tester> tests = new HashMap<>();
  55. tests.put("type", TypeTester.fromType("string"));
  56. tests.put("maxLength", new SizeTester(STRING, s -> s <= 30));
  57. tests.put("minLength", new SizeTester(STRING, s -> s >= 10));
  58. tests.put("pattern", j -> j.asText().matches("^https://.*"));
  59. tests.put("format", FormatTester.forCode("uri"));
  60. return new Schema(tests);
  61. }
  62. private Schema getArraySchema() {
  63. Map<String, Tester> tests = new HashMap<>();
  64. tests.put("type", TypeTester.fromType("array"));
  65. tests.put("maxItems", new SizeTester(ARRAY, s -> s <= 3));
  66. tests.put("minItems", new SizeTester(ARRAY, s -> s >= 1));
  67. tests.put("uniqueItems", new UniqueItemTester());
  68. tests.put("contains", new ContainsTester(getNumberSchema()));
  69. return new Schema(tests);
  70. }
  71. private Schema getObjectSchema() {
  72. Map<String, Tester> tests = new HashMap<>();
  73. tests.put("type", TypeTester.fromType("object"));
  74. tests.put("maxProperties", new SizeTester(OBJECT, s -> s <= 3));
  75. tests.put("minProperties", new SizeTester(OBJECT, s -> s >= 2));
  76. tests.put("required", json -> json.has("string"));
  77. tests.put("propertyNames", new PropertyNameTester(j -> j.asText().matches("^[a-z]*$")));
  78. // TODO Dependencies
  79. return new Schema(tests);
  80. }
  81. @Test
  82. public void testRejectsWrongTypeOnlyIfHasTypeArg() {
  83. Map<String, Tester> tests = new HashMap<>();
  84. tests.put("minProperties", FixedTester.REJECT);
  85. assertThat(new Schema(tests), accepts(NullNode.getInstance()));
  86. tests.put("type", TypeTester.fromType("string"));
  87. assertThat(new Schema(tests), not(accepts(NullNode.getInstance())));
  88. }
  89. @Test
  90. public void testHandlesUniversalChecksForBoolean() {
  91. assertThat(getConstSchema(BooleanNode.TRUE), accepts(BooleanNode.TRUE));
  92. assertThat(getConstSchema(BooleanNode.TRUE),
  93. not(accepts(NullNode.getInstance())));
  94. assertThat(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE),
  95. accepts(BooleanNode.TRUE));
  96. assertThat(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE),
  97. accepts(BooleanNode.FALSE));
  98. assertThat(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE),
  99. not(accepts(NullNode.getInstance())));
  100. }
  101. @Test
  102. public void testHandlesMultipleTestsForNumber() {
  103. assertThat(getNumberSchema(), not(accepts(new DoubleNode(-0.25))));
  104. assertThat(getNumberSchema(), accepts(new DoubleNode(0.0)));
  105. assertThat(getNumberSchema(), accepts(new DoubleNode(0.5)));
  106. assertThat(getNumberSchema(), not(accepts(new DoubleNode(0.6))));
  107. assertThat(getNumberSchema(), not(accepts(new DoubleNode(1.0))));
  108. }
  109. @Test
  110. public void testHandlesOtherNumberChecks() {
  111. Map<String, Tester> tests = new HashMap<>();
  112. tests.put("exclusiveMinimum", new NumberTester(d -> d > 0.0));
  113. tests.put("maximum", new NumberTester(d -> d <= 1.0));
  114. Schema schema = new Schema(tests);
  115. assertThat(schema, not(accepts(new DoubleNode(0.0))));
  116. assertThat(schema, accepts(new DoubleNode(0.5)));
  117. assertThat(schema, accepts(new DoubleNode(0.6)));
  118. assertThat(schema, accepts(new DoubleNode(1.0)));
  119. assertThat(schema, not(accepts(new DoubleNode(1.1))));
  120. }
  121. @Test
  122. public void testHandlesUniversalChecksForNumber() {
  123. assertThat(getConstSchema(new DoubleNode(0.0)), accepts(new DoubleNode(0.0)));
  124. assertThat(getConstSchema(new DoubleNode(0.0)), not(accepts(new DoubleNode(0.5))));
  125. assertThat(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0)),
  126. accepts(new DoubleNode(0.0)));
  127. assertThat(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0)),
  128. accepts(new DoubleNode(1.0)));
  129. assertThat(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0)),
  130. not(accepts(new DoubleNode(0.5))));
  131. }
  132. @Test
  133. public void testHandlesMultipleTestsForString() {
  134. assertThat(getStringSchema(), not(accepts(new TextNode("https://a"))));
  135. assertThat(getStringSchema(), accepts(new TextNode("https://google.com")));
  136. assertThat(getStringSchema(), not(accepts(new TextNode("http://google.com"))));
  137. assertThat(getStringSchema(), not(accepts(new TextNode("https://google.com/query?q=Hippopotomonstrosesquippedaliophobia"))));
  138. }
  139. @Test
  140. public void testHandlesUniversalChecksForString() {
  141. assertThat(getConstSchema(new TextNode("A")), accepts(new TextNode("A")));
  142. assertThat(getConstSchema(new TextNode("A")), not(accepts(new TextNode("C"))));
  143. assertThat(getEnumSchema(new TextNode("A"), new TextNode("B")),
  144. accepts(new TextNode("A")));
  145. assertThat(getEnumSchema(new TextNode("A"), new TextNode("B")),
  146. accepts(new TextNode("B")));
  147. assertThat(getEnumSchema(new TextNode("A"), new TextNode("B")),
  148. not(accepts(new TextNode("C"))));
  149. }
  150. @Test
  151. public void testHandlesMultipleTestsForArray() {
  152. final ArrayNode array = new ArrayNode(JsonNodeFactory.instance);
  153. assertThat(getArraySchema(), not(accepts(array)));
  154. array.add("Test");
  155. assertThat(getArraySchema(), not(accepts(array)));
  156. array.add(0.5);
  157. assertThat(getArraySchema(), accepts(array));
  158. array.add(0.5);
  159. assertThat(getArraySchema(), not(accepts(array)));
  160. array.remove(2);
  161. array.add(true);
  162. array.add(false);
  163. assertThat(getArraySchema(), not(accepts(array)));
  164. }
  165. @Test
  166. public void testHandlesAdditionalMatcherWhenArrayItems() {
  167. Map<String, Tester> tests = new HashMap<>();
  168. tests.put("items", new ItemsTester(getNumberSchema(), getStringSchema()));
  169. tests.put("additionalItems", FixedTester.REJECT);
  170. Schema schema = new Schema(tests);
  171. final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
  172. node.add(0.5);
  173. node.add("https://google.com");
  174. assertThat(schema, accepts(node));
  175. node.add(true);
  176. assertThat(schema, not(accepts(node)));
  177. tests.put("additionalItems", FixedTester.ACCEPT);
  178. assertThat(new Schema(tests), accepts(node));
  179. tests.remove("additionalItems");
  180. assertThat(new Schema(tests), accepts(node));
  181. }
  182. @Test
  183. public void testHandlesAdditionalNotCalledIfFailsInItems() {
  184. Map<String, Tester> tests = new HashMap<>();
  185. tests.put("items", new ItemsTester(getNumberSchema(), getStringSchema()));
  186. tests.put("additionalItems", FixedTester.ACCEPT);
  187. Schema schema = new Schema(tests);
  188. final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
  189. node.add(0.5);
  190. node.add("https://a");
  191. node.add(true);
  192. assertThat(schema, not(accepts(node)));
  193. }
  194. @Test
  195. public void testHandlesUniversalChecksForArray() {
  196. final ArrayNode expected = new ArrayNode(JsonNodeFactory.instance);
  197. expected.add(true);
  198. Map<String, Tester> tests = new HashMap<>();
  199. tests.put("const", new EqualsTester(expected));
  200. Schema schema = new Schema(tests);
  201. final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
  202. assertThat(schema, not(accepts(node)));
  203. node.add(true);
  204. assertThat(schema, accepts(node));
  205. }
  206. @Test
  207. public void testHandlesMultipleTestsForObject() {
  208. final ObjectNode object = new ObjectNode(JsonNodeFactory.instance);
  209. assertThat(getObjectSchema(), not(accepts(object)));
  210. object.set("string", new TextNode("https://google.com"));
  211. assertThat(getObjectSchema(), not(accepts(object)));
  212. object.set("float", new DoubleNode(0.5));
  213. assertThat(getObjectSchema(), accepts(object));
  214. object.set("Caps", BooleanNode.TRUE);
  215. assertThat(getObjectSchema(), not(accepts(object)));
  216. object.remove("Caps");
  217. object.set("bool", BooleanNode.TRUE);
  218. assertThat(getObjectSchema(), accepts(object));
  219. object.set("null", NullNode.getInstance());
  220. assertThat(getObjectSchema(), not(accepts(object)));
  221. }
  222. @Test
  223. public void testHandlesAdditionalMatcherWhenObjectProps() {
  224. Map<String, Tester> tests = new HashMap<>();
  225. tests.put("properties", new PropertyTester(Arrays.asList(
  226. new PropertyTester.Pair("float"::equals, getNumberSchema()),
  227. new PropertyTester.Pair("string"::equals, getStringSchema()))));
  228. tests.put("additionalProperties", FixedTester.REJECT);
  229. Schema schema = new Schema(tests);
  230. final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
  231. node.set("float", new DoubleNode(0.5));
  232. node.set("string", new TextNode("https://google.com"));
  233. assertThat(schema, accepts(node));
  234. node.set("boolean", BooleanNode.TRUE);
  235. assertThat(schema, not(accepts(node)));
  236. tests.put("additionalProperties", FixedTester.ACCEPT);
  237. assertThat(new Schema(tests), accepts(node));
  238. tests.remove("additionalProperties");
  239. assertThat(new Schema(tests), accepts(node));
  240. }
  241. @Test
  242. public void testAdditionalPropertiesRunsOnEachUnmatched() {
  243. Map<String, Tester> tests = new HashMap<>();
  244. tests.put("additionalProperties", new AllItemsTester(OBJECT,
  245. TypeTester.fromType("string")));
  246. Schema schema = new Schema(tests);
  247. final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
  248. node.set("string", new TextNode("https://google.com"));
  249. assertThat(schema, accepts(node));
  250. node.set("float", new DoubleNode(0.5));
  251. assertThat(schema, not(accepts(node)));
  252. }
  253. @Test
  254. public void testHandlesAdditionalNotCalledIfFailsInProps() {
  255. Map<String, Tester> tests = new HashMap<>();
  256. tests.put("properties", new PropertyTester(Arrays.asList(
  257. new PropertyTester.Pair("float"::equals, getNumberSchema()),
  258. new PropertyTester.Pair("string"::equals, getStringSchema()))));
  259. tests.put("additionalProperties", FixedTester.ACCEPT);
  260. Schema schema = new Schema(tests);
  261. final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
  262. node.set("float", new DoubleNode(0.5));
  263. node.set("string", new TextNode("https://a"));
  264. node.set("boolean", BooleanNode.TRUE);
  265. assertThat(schema, not(accepts(node)));
  266. }
  267. @Test
  268. public void testHandlesUniversalChecksForObject() {
  269. final ObjectNode expected = new ObjectNode(JsonNodeFactory.instance);
  270. expected.set("A", BooleanNode.TRUE);
  271. Map<String, Tester> tests = new HashMap<>();
  272. tests.put("const", new EqualsTester(expected));
  273. Schema schema = new Schema(tests);
  274. final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
  275. assertThat(schema, not(accepts(node)));
  276. node.set("A", BooleanNode.TRUE);
  277. assertThat(schema, accepts(node));
  278. }}