|
|
@@ -0,0 +1,318 @@
|
|
|
+package org.leumasjaffe.json.schema;
|
|
|
+
|
|
|
+import static com.fasterxml.jackson.databind.node.JsonNodeType.*;
|
|
|
+import static org.hamcrest.core.Is.is;
|
|
|
+import static org.junit.Assert.*;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import org.junit.Test;
|
|
|
+import org.leumasjaffe.json.schema.tester.AllItemsTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.ContainsTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.EqualsTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.FixedTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.FormatTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.ItemsTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.MockTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.NumberTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.PropertyNameTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.PropertyTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.SizeTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.StubTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.TypeTester;
|
|
|
+import org.leumasjaffe.json.schema.tester.UniqueItemTester;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
|
+import com.fasterxml.jackson.databind.node.BooleanNode;
|
|
|
+import com.fasterxml.jackson.databind.node.DoubleNode;
|
|
|
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
|
|
+import com.fasterxml.jackson.databind.node.NullNode;
|
|
|
+import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
|
+import com.fasterxml.jackson.databind.node.TextNode;
|
|
|
+
|
|
|
+// TODO: patternProperties
|
|
|
+// TODO: dependencies
|
|
|
+// TODO: $ref
|
|
|
+// TODO: additionalItems with mixed results
|
|
|
+public class SchemaTest {
|
|
|
+ private Schema getConstSchema(JsonNode json) {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("const", new EqualsTester(json));
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Schema getEnumSchema(JsonNode... jsons) {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("enum", new EqualsTester(jsons));
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Schema getNumberSchema() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("minimum", new NumberTester(d -> d >= 0.0));
|
|
|
+ tests.put("multipleOf", new NumberTester(d -> Math.abs(d % 0.25) < 1E-7));
|
|
|
+ tests.put("exclusiveMaximum", new NumberTester(d -> d < 1.0));
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Schema getStringSchema() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("maxLength", new SizeTester(STRING, s -> s <= 30));
|
|
|
+ tests.put("minLength", new SizeTester(STRING, s -> s >= 10));
|
|
|
+ tests.put("pattern", (StubTester) j -> j.asText().matches("^https://.*"));
|
|
|
+ tests.put("format", FormatTester.forCode("uri"));
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Schema getArraySchema() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("maxItems", new SizeTester(ARRAY, s -> s <= 3));
|
|
|
+ tests.put("minItems", new SizeTester(ARRAY, s -> s >= 1));
|
|
|
+ tests.put("uniqueItems", new UniqueItemTester());
|
|
|
+ tests.put("contains", new ContainsTester(getNumberSchema()));
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Schema getObjectSchema() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("maxProperties", new SizeTester(OBJECT, s -> s <= 3));
|
|
|
+ tests.put("minProperties", new SizeTester(OBJECT, s -> s >= 2));
|
|
|
+ tests.put("required", (StubTester) json -> json.has("string"));
|
|
|
+ tests.put("propertyNames", new PropertyNameTester((StubTester) j -> j.asText().matches("^[a-z]*$")));
|
|
|
+ // TODO Dependencies
|
|
|
+ return new Schema(tests);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testAcceptsAnyIfNoMatchers() {
|
|
|
+ assertThat(new Schema().acceptedTypes().length, is(Tester.ANY.length));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testRejectsWrongType() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("example", new MockTester(STRING, FixedTester.ACCEPT));
|
|
|
+ assertFalse(new Schema(tests).accepts(NullNode.getInstance()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesUniversalChecksForBoolean() {
|
|
|
+ assertTrue(getConstSchema(BooleanNode.TRUE).accepts(BooleanNode.TRUE));
|
|
|
+ assertFalse(getConstSchema(BooleanNode.TRUE).accepts(NullNode.getInstance()));
|
|
|
+ assertTrue(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE)
|
|
|
+ .accepts(BooleanNode.TRUE));
|
|
|
+ assertTrue(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE)
|
|
|
+ .accepts(BooleanNode.FALSE));
|
|
|
+ assertFalse(getEnumSchema(BooleanNode.TRUE, BooleanNode.FALSE)
|
|
|
+ .accepts(NullNode.getInstance()));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesMultipleTestsForNumber() {
|
|
|
+ assertFalse(getNumberSchema().accepts(new DoubleNode(-0.25)));
|
|
|
+ assertTrue(getNumberSchema().accepts(new DoubleNode(0.0)));
|
|
|
+ assertTrue(getNumberSchema().accepts(new DoubleNode(0.5)));
|
|
|
+ assertFalse(getNumberSchema().accepts(new DoubleNode(0.6)));
|
|
|
+ assertFalse(getNumberSchema().accepts(new DoubleNode(1.0)));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesOtherNumberChecks() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("exclusiveMinimum", new NumberTester(d -> d > 0.0));
|
|
|
+ tests.put("maximum", new NumberTester(d -> d <= 1.0));
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ assertFalse(schema.accepts(new DoubleNode(0.0)));
|
|
|
+ assertTrue(schema.accepts(new DoubleNode(0.5)));
|
|
|
+ assertTrue(schema.accepts(new DoubleNode(0.6)));
|
|
|
+ assertTrue(schema.accepts(new DoubleNode(1.0)));
|
|
|
+ assertFalse(schema.accepts(new DoubleNode(1.1)));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesUniversalChecksForNumber() {
|
|
|
+ assertTrue(getConstSchema(new DoubleNode(0.0)).accepts(new DoubleNode(0.0)));
|
|
|
+ assertFalse(getConstSchema(new DoubleNode(0.0)).accepts(new DoubleNode(0.5)));
|
|
|
+ assertTrue(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0))
|
|
|
+ .accepts(new DoubleNode(0.0)));
|
|
|
+ assertTrue(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0))
|
|
|
+ .accepts(new DoubleNode(1.0)));
|
|
|
+ assertFalse(getEnumSchema(new DoubleNode(0.0), new DoubleNode(1.0))
|
|
|
+ .accepts(new DoubleNode(0.5)));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesMultipleTestsForString() {
|
|
|
+ assertFalse(getStringSchema().accepts(new TextNode("https://a")));
|
|
|
+ assertTrue(getStringSchema().accepts(new TextNode("https://google.com")));
|
|
|
+ assertFalse(getStringSchema().accepts(new TextNode("http://google.com")));
|
|
|
+ assertFalse(getStringSchema().accepts(new TextNode("https://google.com/query?q=Hippopotomonstrosesquippedaliophobia")));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesUniversalChecksForString() {
|
|
|
+ assertTrue(getConstSchema(new TextNode("A")).accepts(new TextNode("A")));
|
|
|
+ assertFalse(getConstSchema(new TextNode("A")).accepts(new TextNode("C")));
|
|
|
+ assertTrue(getEnumSchema(new TextNode("A"), new TextNode("B"))
|
|
|
+ .accepts(new TextNode("A")));
|
|
|
+ assertTrue(getEnumSchema(new TextNode("A"), new TextNode("B"))
|
|
|
+ .accepts(new TextNode("B")));
|
|
|
+ assertFalse(getEnumSchema(new TextNode("A"), new TextNode("B"))
|
|
|
+ .accepts(new TextNode("C")));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesMultipleTestsForArray() {
|
|
|
+ final ArrayNode array = new ArrayNode(JsonNodeFactory.instance);
|
|
|
+ assertFalse(getArraySchema().accepts(array));
|
|
|
+ array.add("Test");
|
|
|
+ assertFalse(getArraySchema().accepts(array));
|
|
|
+ array.add(0.5);
|
|
|
+ assertTrue(getArraySchema().accepts(array));
|
|
|
+ array.add(0.5);
|
|
|
+ assertFalse(getArraySchema().accepts(array));
|
|
|
+ array.remove(2);
|
|
|
+ array.add(true);
|
|
|
+ array.add(false);
|
|
|
+ assertFalse(getArraySchema().accepts(array));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesAdditionalMatcherWhenArrayItems() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("items", new ItemsTester(getNumberSchema(), getStringSchema()));
|
|
|
+ tests.put("additionalItems", FixedTester.REJECT);
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
|
|
|
+ node.add(0.5);
|
|
|
+ node.add("https://google.com");
|
|
|
+ assertTrue(schema.accepts(node));
|
|
|
+ node.add(true);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+
|
|
|
+ tests.put("additionalItems", FixedTester.ACCEPT);
|
|
|
+ assertTrue(new Schema(tests).accepts(node));
|
|
|
+
|
|
|
+ tests.remove("additionalItems");
|
|
|
+ assertTrue(new Schema(tests).accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesAdditionalNotCalledIfFailsInItems() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("items", new ItemsTester(getNumberSchema(), getStringSchema()));
|
|
|
+ tests.put("additionalItems", FixedTester.ACCEPT);
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
|
|
|
+ node.add(0.5);
|
|
|
+ node.add("https://a");
|
|
|
+ node.add(true);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesUniversalChecksForArray() {
|
|
|
+ final ArrayNode expected = new ArrayNode(JsonNodeFactory.instance);
|
|
|
+ expected.add(true);
|
|
|
+
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("const", new EqualsTester(expected));
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ArrayNode node = new ArrayNode(JsonNodeFactory.instance);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+ node.add(true);
|
|
|
+ assertTrue(schema.accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesMultipleTestsForObject() {
|
|
|
+ final ObjectNode object = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ assertFalse(getObjectSchema().accepts(object));
|
|
|
+ object.set("string", new TextNode("https://google.com"));
|
|
|
+ assertFalse(getObjectSchema().accepts(object));
|
|
|
+ object.set("float", new DoubleNode(0.5));
|
|
|
+ assertTrue(getObjectSchema().accepts(object));
|
|
|
+ object.set("Caps", BooleanNode.TRUE);
|
|
|
+ assertFalse(getObjectSchema().accepts(object));
|
|
|
+ object.remove("Caps");
|
|
|
+ object.set("bool", BooleanNode.TRUE);
|
|
|
+ assertTrue(getObjectSchema().accepts(object));
|
|
|
+ object.set("null", NullNode.getInstance());
|
|
|
+ assertFalse(getObjectSchema().accepts(object));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesAdditionalMatcherWhenObjectProps() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("properties", new PropertyTester(Arrays.asList(
|
|
|
+ new PropertyTester.Pair("float"::equals, getNumberSchema()),
|
|
|
+ new PropertyTester.Pair("string"::equals, getStringSchema()))));
|
|
|
+ tests.put("additionalProperties", FixedTester.REJECT);
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ node.set("float", new DoubleNode(0.5));
|
|
|
+ node.set("string", new TextNode("https://google.com"));
|
|
|
+ assertTrue(schema.accepts(node));
|
|
|
+ node.set("boolean", BooleanNode.TRUE);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+
|
|
|
+ tests.put("additionalProperties", FixedTester.ACCEPT);
|
|
|
+ assertTrue(new Schema(tests).accepts(node));
|
|
|
+
|
|
|
+ tests.remove("additionalProperties");
|
|
|
+ assertTrue(new Schema(tests).accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testAdditionalPropertiesRunsOnEachUnmatched() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("additionalProperties", new AllItemsTester(OBJECT,
|
|
|
+ TypeTester.fromType("string")));
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ node.set("string", new TextNode("https://google.com"));
|
|
|
+ assertTrue(schema.accepts(node));
|
|
|
+ node.set("float", new DoubleNode(0.5));
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesAdditionalNotCalledIfFailsInProps() {
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("properties", new PropertyTester(Arrays.asList(
|
|
|
+ new PropertyTester.Pair("float"::equals, getNumberSchema()),
|
|
|
+ new PropertyTester.Pair("string"::equals, getStringSchema()))));
|
|
|
+ tests.put("additionalProperties", FixedTester.ACCEPT);
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ node.set("float", new DoubleNode(0.5));
|
|
|
+ node.set("string", new TextNode("https://a"));
|
|
|
+ node.set("boolean", BooleanNode.TRUE);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testHandlesUniversalChecksForObject() {
|
|
|
+ final ObjectNode expected = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ expected.set("A", BooleanNode.TRUE);
|
|
|
+
|
|
|
+ Map<String, Tester> tests = new HashMap<>();
|
|
|
+ tests.put("const", new EqualsTester(expected));
|
|
|
+ Schema schema = new Schema(tests);
|
|
|
+
|
|
|
+ final ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
|
|
|
+ assertFalse(schema.accepts(node));
|
|
|
+ node.set("A", BooleanNode.TRUE);
|
|
|
+ assertTrue(schema.accepts(node));
|
|
|
+ }}
|