From cdb9526b90468a63a911d6a19e5b6de2b64e1bff Mon Sep 17 00:00:00 2001 From: Gerrett Date: Sun, 19 May 2024 16:43:55 -0500 Subject: [PATCH] feat: Adding basic error type support for output units. Signed-off-by: Gerrett --- .../json/schema/OutputUnitConverter.java | 8 ++ .../schema/JsonSchemaValidationException.java | 21 +++- .../io/vertx/json/schema/OutputErrorType.java | 12 ++ .../java/io/vertx/json/schema/OutputUnit.java | 18 ++- .../json/schema/impl/SchemaValidatorImpl.java | 117 +++++++++--------- .../java/io/vertx/json/schema/CBORTest.java | 2 + .../json/schema/DynamicReferenceTest.java | 1 + .../io/vertx/json/schema/OutputUnitTest.java | 19 ++- .../io/vertx/json/schema/ResolverTest.java | 2 + .../SchemaDefinitionValidationTest.java | 1 + .../io/vertx/json/schema/ValidationTest.java | 18 ++- .../io/vertx/json/schema/ValidatorTest.java | 11 +- 12 files changed, 156 insertions(+), 74 deletions(-) create mode 100644 src/main/java/io/vertx/json/schema/OutputErrorType.java diff --git a/src/main/generated/io/vertx/json/schema/OutputUnitConverter.java b/src/main/generated/io/vertx/json/schema/OutputUnitConverter.java index 1328bd42..2ab1440a 100644 --- a/src/main/generated/io/vertx/json/schema/OutputUnitConverter.java +++ b/src/main/generated/io/vertx/json/schema/OutputUnitConverter.java @@ -65,6 +65,11 @@ static void fromJson(Iterable> json, OutputU obj.setAnnotations(list); } break; + case "errorType": + if (member.getValue() instanceof String) { + obj.setErrorType(io.vertx.json.schema.OutputErrorType.valueOf((String)member.getValue())); + } + break; } } } @@ -99,5 +104,8 @@ static void toJson(OutputUnit obj, java.util.Map json) { obj.getAnnotations().forEach(item -> array.add(item.toJson())); json.put("annotations", array); } + if (obj.getErrorType() != null) { + json.put("errorType", obj.getErrorType().name()); + } } } diff --git a/src/main/java/io/vertx/json/schema/JsonSchemaValidationException.java b/src/main/java/io/vertx/json/schema/JsonSchemaValidationException.java index 8687bd3a..92124962 100644 --- a/src/main/java/io/vertx/json/schema/JsonSchemaValidationException.java +++ b/src/main/java/io/vertx/json/schema/JsonSchemaValidationException.java @@ -18,19 +18,24 @@ public final class JsonSchemaValidationException extends Exception { final private String location; + final private OutputErrorType errorType; - public JsonSchemaValidationException(String message, Throwable cause, String location) { + public JsonSchemaValidationException(String message, Throwable cause, String location, OutputErrorType errorType) { super(message, cause); this.location = location; + this.errorType = errorType; } - public JsonSchemaValidationException(String message, String location, StackTraceElement stackTraceElement) { - this(message, null, location, stackTraceElement); + public JsonSchemaValidationException(String message, String location, OutputErrorType errorType, + StackTraceElement stackTraceElement) { + this(message, null, location, errorType, stackTraceElement); } - public JsonSchemaValidationException(String message, Throwable cause, String location, StackTraceElement stackTraceElement) { + public JsonSchemaValidationException(String message, Throwable cause, String location, OutputErrorType errorType, + StackTraceElement stackTraceElement) { super(message, cause, stackTraceElement != null, stackTraceElement != null); this.location = location; + this.errorType = errorType; if (stackTraceElement != null) { setStackTrace(new StackTraceElement[]{ stackTraceElement @@ -45,4 +50,12 @@ public JsonSchemaValidationException(String message, Throwable cause, String loc public String location() { return location; } + + /** + * @return our best guess on what the validation error type is. + */ + public OutputErrorType errorType() { + return errorType; + } + } diff --git a/src/main/java/io/vertx/json/schema/OutputErrorType.java b/src/main/java/io/vertx/json/schema/OutputErrorType.java new file mode 100644 index 00000000..0f4a67a6 --- /dev/null +++ b/src/main/java/io/vertx/json/schema/OutputErrorType.java @@ -0,0 +1,12 @@ +package io.vertx.json.schema; + +import io.vertx.codegen.annotations.VertxGen; + +@VertxGen +public enum OutputErrorType { + + NONE, + INVALID_VALUE, + MISSING_VALUE + +} diff --git a/src/main/java/io/vertx/json/schema/OutputUnit.java b/src/main/java/io/vertx/json/schema/OutputUnit.java index 13d55536..f4530ad8 100644 --- a/src/main/java/io/vertx/json/schema/OutputUnit.java +++ b/src/main/java/io/vertx/json/schema/OutputUnit.java @@ -29,6 +29,7 @@ public class OutputUnit { private String keywordLocation; private String instanceLocation; private String error; + private OutputErrorType errorType = OutputErrorType.NONE; private List errors; private List annotations; @@ -44,11 +45,12 @@ public OutputUnit(boolean valid) { this.valid = valid; } - public OutputUnit(String instanceLocation, String absoluteKeywordLocation, String keywordLocation, String error) { + public OutputUnit(String instanceLocation, String absoluteKeywordLocation, String keywordLocation, String error, OutputErrorType errorType) { this.instanceLocation = instanceLocation; this.absoluteKeywordLocation = absoluteKeywordLocation; this.keywordLocation = keywordLocation; this.error = error; + this.errorType = errorType; } public Boolean getValid() { @@ -132,6 +134,15 @@ public OutputUnit setAnnotations(List annotations) { return this; } + public OutputErrorType getErrorType() { + return errorType; + } + + public OutputUnit setErrorType(OutputErrorType errorType) { + this.errorType = errorType; + return this; + } + public OutputUnit addAnnotation(OutputUnit annotation) { if (this.annotations == null) { this.annotations = new ArrayList<>(); @@ -161,6 +172,7 @@ public void checkValidity() throws JsonSchemaValidationException { throw new JsonSchemaValidationException( msg == null ? "JsonSchema Validation error" : msg, location, + errorType, // add some information to the stack trace createStackTraceElement()); } else { @@ -173,6 +185,7 @@ public void checkValidity() throws JsonSchemaValidationException { throw new JsonSchemaValidationException( msg == null ? "JsonSchema Validation error" : msg, location, + errorType, // add some information to the stack trace createStackTraceElement()); } else { @@ -186,6 +199,7 @@ public void checkValidity() throws JsonSchemaValidationException { error.getError(), lastException, location, + errorType, // add some information to the stack trace error.createStackTraceElement()); lastException = cause; @@ -194,7 +208,7 @@ public void checkValidity() throws JsonSchemaValidationException { throw lastException; } else { // one final wrap as there is extra error message in the unit - throw new JsonSchemaValidationException(msg, lastException, getAbsoluteKeywordLocation()); + throw new JsonSchemaValidationException(msg, lastException, getAbsoluteKeywordLocation(), errorType); } } } diff --git a/src/main/java/io/vertx/json/schema/impl/SchemaValidatorImpl.java b/src/main/java/io/vertx/json/schema/impl/SchemaValidatorImpl.java index 075b35bb..f77806a2 100644 --- a/src/main/java/io/vertx/json/schema/impl/SchemaValidatorImpl.java +++ b/src/main/java/io/vertx/json/schema/impl/SchemaValidatorImpl.java @@ -89,7 +89,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (schema == BooleanSchema.TRUE) { return new OutputUnit(true); } else { - return new OutputUnit(false); + return new OutputUnit(false).setErrorType(OutputErrorType.INVALID_VALUE); } } @@ -139,7 +139,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$recursiveRef"), baseLocation + "/$recursiveRef", "A sub-schema had errors")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$recursiveRef"), baseLocation + "/$recursiveRef", "A sub-schema had errors", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -172,7 +172,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$dynamicRef"), baseLocation + "/$dynamicRef", "A sub-schema had errors")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$dynamicRef"), baseLocation + "/$dynamicRef", "A sub-schema had errors", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -183,7 +183,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin .get(dynamicAnchor) .removeLast(); } - return new OutputUnit(errors.isEmpty()).setErrors(errors); + return new OutputUnit(errors.isEmpty()).setErrors(errors).setErrorType(errors.isEmpty() ? OutputErrorType.NONE : errors.get(0).getErrorType()); } } } @@ -212,14 +212,15 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$ref"), baseLocation + "/$ref", "A subschema had errors")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/$ref"), baseLocation + "/$ref", "A subschema had errors", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } } if (draft == Draft.DRAFT4 || draft == Draft.DRAFT7) { return new OutputUnit(errors.isEmpty()) - .setErrors(outputFormat == OutputFormat.Flag ? null : errors.isEmpty() ? null : errors); + .setErrors(outputFormat == OutputFormat.Flag ? null : errors.isEmpty() ? null : errors) + .setErrorType(outputFormat == OutputFormat.Flag ? null : errors.isEmpty() ? OutputErrorType.NONE : errors.get(0).getErrorType()); } } @@ -236,33 +237,33 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin } } if (!valid) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + String.join(", ", type.getList()))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + String.join(", ", type.getList()), OutputErrorType.INVALID_VALUE)); } } else if ("integer".equals(schema.get("type"))) { if (!"number".equals(instanceType) || !Numbers.isInteger(instance)) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + schema.get("type"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + schema.get("type"), OutputErrorType.INVALID_VALUE)); } } else if (schema.containsKey("type") && !instanceType.equals(schema.get("type"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + schema.get("type"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/type"), baseLocation + "/type", "Instance type " + instanceType + " is invalid. Expected " + schema.get("type"), OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("const")) { if ("object".equals(instanceType) || "array".equals(instanceType)) { if (!JSON.deepCompare(instance, schema.get("const"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/const"), baseLocation + "/const", "Instance does not match " + Json.encode(schema.get("const")))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/const"), baseLocation + "/const", "Instance does not match " + Json.encode(schema.get("const")), OutputErrorType.INVALID_VALUE)); } } else if (!Utils.Objects.equals(schema.get("const"), instance)) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/const"), baseLocation + "/const", "Instance does not match " + Json.encode(schema.get("const")))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/const"), baseLocation + "/const", "Instance does not match " + Json.encode(schema.get("const")), OutputErrorType.INVALID_VALUE)); } } if (schema.containsKey("enum")) { if ("object".equals(instanceType) || "array".equals(instanceType)) { if (schema.get("enum").stream().noneMatch(value -> JSON.deepCompare(instance, value))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/enum"), baseLocation + "/enum", "Instance does not match any of " + Json.encode(schema.get("enum")))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/enum"), baseLocation + "/enum", "Instance does not match any of " + Json.encode(schema.get("enum")), OutputErrorType.INVALID_VALUE)); } } else if (schema.get("enum").stream().noneMatch(value -> Utils.Objects.equals(instance, value))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/enum"), baseLocation + "/enum", "Instance does not match any of " + Json.encode(schema.get("enum")))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/enum"), baseLocation + "/enum", "Instance does not match any of " + Json.encode(schema.get("enum")), OutputErrorType.INVALID_VALUE)); } } @@ -278,7 +279,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/not"), baseLocation + "/not", "Instance matched \"not\" schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/not"), baseLocation + "/not", "Instance matched \"not\" schema", result.getErrorType())); } } @@ -310,7 +311,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (anyValid) { errors = errors.subList(0, Math.min(errors.size(), errorsLength)); } else { - errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/anyOf"), baseLocation + "/anyOf", "Instance does not match any subschemas")); + errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/anyOf"), baseLocation + "/anyOf", "Instance does not match any subschemas", OutputErrorType.INVALID_VALUE)); } } @@ -340,7 +341,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (allValid) { errors = errors.subList(0, Math.min(errors.size(), errorsLength)); } else { - errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/allOf"), baseLocation + "/allOf", "Instance does not match every subschema")); + errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/allOf"), baseLocation + "/allOf", "Instance does not match every subschema", OutputErrorType.INVALID_VALUE)); } } @@ -372,7 +373,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (matches == 1) { errors = errors.subList(0, Math.min(errors.size(), errorsLength)); } else { - errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/oneOf"), baseLocation + "/oneOf", "Instance does not match exactly one subschema (" + matches + " matches)")); + errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/oneOf"), baseLocation + "/oneOf", "Instance does not match exactly one subschema (" + matches + " matches)", OutputErrorType.INVALID_VALUE)); } } @@ -404,7 +405,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!thenResult.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/if"), baseLocation + "/if", "Instance does not match \"then\" schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/if"), baseLocation + "/if", "Instance does not match \"then\" schema", thenResult.getErrorType())); if (thenResult.getErrors() != null) { errors.addAll(thenResult.getErrors()); } @@ -422,7 +423,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!elseResult.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/else"), baseLocation + "/else", "Instance does not match \"else\" schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/else"), baseLocation + "/else", "Instance does not match \"else\" schema", elseResult.getErrorType())); if (elseResult.getErrors() != null) { errors.addAll(elseResult.getErrors()); } @@ -435,7 +436,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (schema.containsKey("required")) { for (final Object key : schema.get("required")) { if (!((JsonObject) instance).containsKey((String) key)) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/required"), baseLocation + "/required", "Instance does not have required property \"" + key + "\"")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/required"), baseLocation + "/required", "Instance does not have required property \"" + key + "\"", OutputErrorType.MISSING_VALUE)); } } } @@ -443,11 +444,11 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin final Set keys = ((JsonObject) instance).fieldNames(); if (schema.containsKey("minProperties") && keys.size() < schema.get("minProperties")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minProperties"), baseLocation + "/minProperties", "Instance does not have at least " + schema.get("minProperties") + " properties")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minProperties"), baseLocation + "/minProperties", "Instance does not have at least " + schema.get("minProperties") + " properties", OutputErrorType.MISSING_VALUE)); } if (schema.containsKey("maxProperties") && keys.size() > schema.get("maxProperties")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxProperties"), baseLocation + "/maxProperties", "Instance does not have at least " + schema.get("maxProperties") + " properties")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxProperties"), baseLocation + "/maxProperties", "Instance does not have at least " + schema.get("maxProperties") + " properties", OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("propertyNames")) { @@ -464,7 +465,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/propertyNames"), baseLocation + "/propertyNames", "Property name \"" + key + "\" does not match schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/propertyNames"), baseLocation + "/propertyNames", "Property name \"" + key + "\" does not match schema", OutputErrorType.INVALID_VALUE)); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -478,7 +479,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin final JsonArray required = schema.get("dependentRequired").getJsonArray(key); for (final Object dependantKey : required) { if (!(((JsonObject) instance).containsKey((String) dependantKey))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependentRequired"), baseLocation + "/dependentRequired", "Instance has \"" + key + "\" but does not have \"" + dependantKey + "\"")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependentRequired"), baseLocation + "/dependentRequired", "Instance has \"" + key + "\" but does not have \"" + dependantKey + "\"", OutputErrorType.MISSING_VALUE)); } } } @@ -499,7 +500,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependentSchemas"), baseLocation + "/dependentSchemas", "Instance has \"" + key + "\" but does not match dependant schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependentSchemas"), baseLocation + "/dependentSchemas", "Instance has \"" + key + "\" but does not match dependant schema", OutputErrorType.MISSING_VALUE)); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -515,7 +516,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (propsOrSchema instanceof JsonArray) { for (final Object dependantKey : ((JsonArray) propsOrSchema)) { if (!((JsonObject) instance).containsKey((String) dependantKey)) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependencies"), baseLocation + "/dependencies", "Instance has \"" + key + "\" but does not have \"" + dependantKey + "\"")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependencies"), baseLocation + "/dependencies", "Instance has \"" + key + "\" but does not have \"" + dependantKey + "\"", OutputErrorType.MISSING_VALUE)); } } } else { @@ -530,7 +531,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin dynamicContext ); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependencies"), baseLocation + "/dependencies", "Instance has \"" + key + "\" but does not match dependant schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/dependencies"), baseLocation + "/dependencies", "Instance has \"" + key + "\" but does not match dependant schema", OutputErrorType.MISSING_VALUE)); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -565,7 +566,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin thisEvaluated.add(key); } else { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/properties"), baseLocation + "/properties", "Property \"" + key + "\" does not match schema")); + errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/properties"), baseLocation + "/properties", "Property \"" + key + "\" does not match schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -599,7 +600,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin thisEvaluated.add(key); } else { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/patternProperties"), baseLocation + "/patternProperties", "Property \"" + key + "\" matches pattern \"" + pattern + "\" but does not match associated schema")); + errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/patternProperties"), baseLocation + "/patternProperties", "Property \"" + key + "\" matches pattern \"" + pattern + "\" but does not match associated schema", OutputErrorType.MISSING_VALUE)); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -628,7 +629,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin evaluated.add(key); } else { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/additionalProperties"), baseLocation + "/additionalProperties", "Property \"" + key + "\" does not match additional properties schema")); + errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/additionalProperties"), baseLocation + "/additionalProperties", "Property \"" + key + "\" does not match additional properties schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -654,7 +655,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (result.getValid()) { evaluated.add(key); } else { - errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/unevaluatedProperties"), baseLocation + "/unevaluatedProperties", "Property \"" + key + "\" does not match unevaluated properties schema")); + errors.add(new OutputUnit(subInstancePointer, computeAbsoluteKeywordLocation(schema, schemaLocation + "/unevaluatedProperties"), baseLocation + "/unevaluatedProperties", "Property \"" + key + "\" does not match unevaluated properties schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -666,11 +667,11 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin } case "array": { if (schema.containsKey("maxItems") && ((JsonArray) instance).size() > schema.get("maxItems")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxItems"), baseLocation + "/maxItems", "Array has too many items ( + " + ((JsonArray) instance).size() + " > " + schema.get("maxItems") + ")")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxItems"), baseLocation + "/maxItems", "Array has too many items ( + " + ((JsonArray) instance).size() + " > " + schema.get("maxItems") + ")", OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("minItems") && ((JsonArray) instance).size() < schema.get("minItems")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minItems"), baseLocation + "/minItems", "Array has too few items ( + " + ((JsonArray) instance).size() + " < " + schema.get("minItems") + ")")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minItems"), baseLocation + "/minItems", "Array has too few items ( + " + ((JsonArray) instance).size() + " < " + schema.get("minItems") + ")", OutputErrorType.MISSING_VALUE)); } final int length = ((JsonArray) instance).size(); @@ -693,7 +694,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin evaluated.add(i); if (!result.getValid()) { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/prefixItems"), baseLocation + "/prefixItems", "Items did not match schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/prefixItems"), baseLocation + "/prefixItems", "Items did not match schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -721,7 +722,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin evaluated.add(i); if (!result.getValid()) { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/items"), baseLocation + "/items", "Items did not match schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/items"), baseLocation + "/items", "Items did not match schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -745,7 +746,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin evaluated.add(i); if (!result.getValid()) { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/items"), baseLocation + "/items", "Items did not match schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/items"), baseLocation + "/items", "Items did not match schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -772,7 +773,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin evaluated.add(i); if (!result.getValid()) { stop = outputFormat == OutputFormat.Flag; - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/additionalItems"), schemaLocation + "/additionalItems", "Items did not match additional items schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/additionalItems"), schemaLocation + "/additionalItems", "Items did not match additional items schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -783,9 +784,9 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin if (schema.containsKey("contains")) { if (length == 0 && !schema.containsKey("minContains")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/contains"), baseLocation + "/contains", "Array is empty. It must contain at least one item matching the schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/contains"), baseLocation + "/contains", "Array is empty. It must contain at least one item matching the schema", OutputErrorType.MISSING_VALUE)); } else if (schema.containsKey("minContains") && length < schema.get("minContains")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minContains"), baseLocation + "/minContains", "Array has less items (" + length + ") than minContains (" + schema.get("minContains") + ")")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minContains"), baseLocation + "/minContains", "Array has less items (" + length + ") than minContains (" + schema.get("minContains") + ")", OutputErrorType.MISSING_VALUE)); } else { final int errorsLength = errors.size(); int contained = 0; @@ -819,11 +820,11 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin !schema.containsKey("maxContains") && contained == 0 ) { - errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/contains"), baseLocation + "/contains", "Array does not contain item matching schema")); + errors.add(errorsLength, new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/contains"), baseLocation + "/contains", "Array does not contain item matching schema", OutputErrorType.INVALID_VALUE)); } else if (schema.containsKey("minContains") && contained < schema.get("minContains")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minContains"), baseLocation + "/minContains", "Array must contain at least " + schema.get("minContains") + " items matching schema. Only " + contained + " items were found")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minContains"), baseLocation + "/minContains", "Array must contain at least " + schema.get("minContains") + " items matching schema. Only " + contained + " items were found", OutputErrorType.MISSING_VALUE)); } else if (schema.containsKey("maxContains") && contained > schema.get("maxContains")) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxContains"), baseLocation + "/maxContains", "Array may contain at most " + schema.get("minContains") + " items matching schema. " + contained + " items were found")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxContains"), baseLocation + "/maxContains", "Array may contain at most " + schema.get("minContains") + " items matching schema. " + contained + " items were found", OutputErrorType.INVALID_VALUE)); } } } @@ -845,7 +846,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin ); evaluated.add(i); if (!result.getValid()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/unevaluatedItems"), baseLocation + "/unevaluatedItems", "Items did not match unevaluated items schema")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/unevaluatedItems"), baseLocation + "/unevaluatedItems", "Items did not match unevaluated items schema", result.getErrorType())); if (result.getErrors() != null) { errors.addAll(result.getErrors()); } @@ -865,7 +866,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin final Object b = ((JsonArray) instance).getValue(k); final boolean bo = "object".equals(JSON.typeOf(b)) && b != null; if (Utils.Objects.equals(a, b) || (ao && bo && JSON.deepCompare(a, b))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/uniqueItems"), baseLocation + "/uniqueItems", "Duplicate items at indexes " + j + " and " + k)); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/uniqueItems"), baseLocation + "/uniqueItems", "Duplicate items at indexes " + j + " and " + k, OutputErrorType.INVALID_VALUE)); break outer; } } @@ -880,27 +881,27 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin ((schema.get("exclusiveMinimum", false) && Numbers.lte((Number) instance, schema.get("minimum"))) || Numbers.lt((Number) instance, schema.get("minimum"))) ) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minimum"), baseLocation + "/minimum", instance + " is less than " + (schema.get("exclusiveMinimum", false) ? "or equal to " : "") + schema.get("minimum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minimum"), baseLocation + "/minimum", instance + " is less than " + (schema.get("exclusiveMinimum", false) ? "or equal to " : "") + schema.get("minimum"), OutputErrorType.INVALID_VALUE)); } if ( schema.containsKey("maximum") && ((schema.get("exclusiveMaximum", false) && Numbers.gte((Number) instance, schema.get("maximum"))) || Numbers.gt((Number) instance, schema.get("maximum"))) ) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maximum"), baseLocation + "/maximum", instance + " is greater than " + (schema.get("exclusiveMaximum", false) ? "or equal to " : "") + schema.get("maximum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maximum"), baseLocation + "/maximum", instance + " is greater than " + (schema.get("exclusiveMaximum", false) ? "or equal to " : "") + schema.get("maximum"), OutputErrorType.INVALID_VALUE)); } } else { if (schema.containsKey("minimum") && Numbers.lt((Number) instance, schema.get("minimum"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minimum"), baseLocation + "/minimum", instance + " is less than " + schema.get("minimum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minimum"), baseLocation + "/minimum", instance + " is less than " + schema.get("minimum"), OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("maximum") && Numbers.gt((Number) instance, schema.get("maximum"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maximum"), baseLocation + "/maximum", instance + " is greater than " + schema.get("maximum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maximum"), baseLocation + "/maximum", instance + " is greater than " + schema.get("maximum"), OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("exclusiveMinimum") && Numbers.lte((Number) instance, schema.get("exclusiveMinimum"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/exclusiveMinimum"), baseLocation + "/exclusiveMinimum", instance + " is less than or equal to " + schema.get("exclusiveMinimum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/exclusiveMinimum"), baseLocation + "/exclusiveMinimum", instance + " is less than or equal to " + schema.get("exclusiveMinimum"), OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("exclusiveMaximum") && Numbers.gte((Number) instance, schema.get("exclusiveMaximum"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/exclusiveMaximum"), baseLocation + "/exclusiveMaximum", instance + " is greater than or equal to " + schema.get("exclusiveMaximum"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/exclusiveMaximum"), baseLocation + "/exclusiveMaximum", instance + " is greater than or equal to " + schema.get("exclusiveMaximum"), OutputErrorType.INVALID_VALUE)); } } if (schema.containsKey("multipleOf")) { @@ -909,7 +910,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin Math.abs(0 - remainder) >= 1.1920929e-7 && Math.abs(schema.get("multipleOf").doubleValue() - remainder) >= 1.1920929e-7 ) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/multipleOf"), baseLocation + "/multipleOf", instance + " is not a multiple of " + schema.get("multipleOf"))); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/multipleOf"), baseLocation + "/multipleOf", instance + " is not a multiple of " + schema.get("multipleOf"), OutputErrorType.INVALID_VALUE)); } } break; @@ -919,19 +920,19 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin ? 0 : Strings.ucs2length((String) instance); if (schema.containsKey("minLength") && Numbers.lt(length, schema.get("minLength"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minLength"), baseLocation + "/minLength", "String is too short (" + length + " < " + schema.get("minLength") + ")")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/minLength"), baseLocation + "/minLength", "String is too short (" + length + " < " + schema.get("minLength") + ")", OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("maxLength") && Numbers.gt(length, schema.get("maxLength"))) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxLength"), baseLocation + "/maxLength", "String is too long (" + length + " > " + schema.get("maxLength") + ")")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/maxLength"), baseLocation + "/maxLength", "String is too long (" + length + " > " + schema.get("maxLength") + ")", OutputErrorType.INVALID_VALUE)); } if (schema.containsKey("pattern") && !Pattern.compile(schema.get("pattern")).matcher((String) instance).find()) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/pattern"), baseLocation + "/pattern", "String does not match pattern")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/pattern"), baseLocation + "/pattern", "String does not match pattern", OutputErrorType.INVALID_VALUE)); } if ( schema.containsKey("format") && !Format.fastFormat(schema.get("format"), (String) instance) ) { - errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/format"), baseLocation + "/format", "String does not match format \"" + schema.get("format") + "\"")); + errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/format"), baseLocation + "/format", "String does not match format \"" + schema.get("format") + "\"", OutputErrorType.INVALID_VALUE)); } break; } @@ -940,7 +941,7 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin String error = formatValidator.validateFormat(instanceType, schema.get("format"), instance); if (error != null) { errors.add(new OutputUnit(instanceLocation, computeAbsoluteKeywordLocation(schema, schemaLocation + "/format"), - baseLocation + "/format", error)); + baseLocation + "/format", error, OutputErrorType.INVALID_VALUE)); } if (dynamicAnchor != null) { @@ -951,7 +952,9 @@ private OutputUnit validate(final Object _instance, final JsonSchema schema, fin return new OutputUnit(errors.isEmpty()) .setErrors(outputFormat == OutputFormat.Flag ? null : errors.isEmpty() ? null : errors) - .setAnnotations(outputFormat == OutputFormat.Flag ? null : annotations.isEmpty() ? null : annotations); + .setAnnotations(outputFormat == OutputFormat.Flag ? null : annotations.isEmpty() ? null : annotations) + .setErrorType((outputFormat == OutputFormat.Flag ? OutputErrorType.NONE : + errors.isEmpty() ? OutputErrorType.NONE : errors.get(0).getErrorType())); } private String computeAbsoluteKeywordLocation(JsonSchema schema, String schemaKeywordLocation) { diff --git a/src/test/java/io/vertx/json/schema/CBORTest.java b/src/test/java/io/vertx/json/schema/CBORTest.java index 476f9ae9..1655a6c9 100644 --- a/src/test/java/io/vertx/json/schema/CBORTest.java +++ b/src/test/java/io/vertx/json/schema/CBORTest.java @@ -64,6 +64,7 @@ public void schemaAndValidationUsingCBOR() { assertThat(res.getValid()).isTrue(); assertThat(res.getErrors()).isNull(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -83,6 +84,7 @@ public void schemaAndValidationUsingCBORInvalid() { assertThat(res.getValid()).isFalse(); assertThat(res.getErrors()).isNotEmpty(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.MISSING_VALUE); try { res.checkValidity(); diff --git a/src/test/java/io/vertx/json/schema/DynamicReferenceTest.java b/src/test/java/io/vertx/json/schema/DynamicReferenceTest.java index 2f244d56..5b201c80 100644 --- a/src/test/java/io/vertx/json/schema/DynamicReferenceTest.java +++ b/src/test/java/io/vertx/json/schema/DynamicReferenceTest.java @@ -133,6 +133,7 @@ public void testDynamicReferenceFromSpec202012() { .validate(instance); assertThat(ou.getValid()).isFalse(); + assertThat(ou.getErrorType()).isEqualByComparingTo(OutputErrorType.INVALID_VALUE); } // @Test diff --git a/src/test/java/io/vertx/json/schema/OutputUnitTest.java b/src/test/java/io/vertx/json/schema/OutputUnitTest.java index 547b782d..42fe96a2 100644 --- a/src/test/java/io/vertx/json/schema/OutputUnitTest.java +++ b/src/test/java/io/vertx/json/schema/OutputUnitTest.java @@ -38,35 +38,41 @@ public void testBasicMode() { " {\n" + " \"keywordLocation\": \"\",\n" + " \"instanceLocation\": \"\",\n" + - " \"error\": \"A subschema had errors.\"\n" + + " \"error\": \"A subschema had errors.\",\n" + + " \"errorType\": \"INVALID_VALUE\"\n" + " },\n" + " {\n" + " \"keywordLocation\": \"/items/$ref\",\n" + " \"absoluteKeywordLocation\":\n" + " \"https://example.com/polygon#/$defs/point\",\n" + " \"instanceLocation\": \"/1\",\n" + - " \"error\": \"A subschema had errors.\"\n" + + " \"error\": \"A subschema had errors.\",\n" + + " \"errorType\": \"INVALID_VALUE\"\n" + " },\n" + " {\n" + " \"keywordLocation\": \"/items/$ref/required\",\n" + " \"absoluteKeywordLocation\":\n" + " \"https://example.com/polygon#/$defs/point/required\",\n" + " \"instanceLocation\": \"/1\",\n" + - " \"error\": \"Required property 'y' not found.\"\n" + + " \"error\": \"Required property 'y' not found.\",\n" + + " \"errorType\": \"MISSING_VALUE\"\n" + " },\n" + " {\n" + " \"keywordLocation\": \"/items/$ref/additionalProperties\",\n" + " \"absoluteKeywordLocation\":\n" + " \"https://example.com/polygon#/$defs/point/additionalProperties\",\n" + " \"instanceLocation\": \"/1/z\",\n" + - " \"error\": \"Additional property 'z' found but was invalid.\"\n" + + " \"error\": \"Additional property 'z' found but was invalid.\",\n" + + " \"errorType\": \"INVALID_VALUE\"\n" + " },\n" + " {\n" + " \"keywordLocation\": \"/minItems\",\n" + " \"instanceLocation\": \"\",\n" + - " \"error\": \"Expected at least 3 items but found 2\"\n" + + " \"error\": \"Expected at least 3 items but found 2\",\n" + + " \"errorType\": \"MISSING_VALUE\"\n" + " }\n" + - " ]\n" + + " ],\n" + + "\"errorType\": \"MISSING_VALUE\"\n" + "}" )); try { @@ -75,6 +81,7 @@ public void testBasicMode() { } catch (JsonSchemaValidationException e) { assertEquals("Expected at least 3 items but found 2", e.getMessage()); assertNull(e.location()); + assertEquals(OutputErrorType.MISSING_VALUE, e.errorType()); } } diff --git a/src/test/java/io/vertx/json/schema/ResolverTest.java b/src/test/java/io/vertx/json/schema/ResolverTest.java index f4f75434..4639d250 100644 --- a/src/test/java/io/vertx/json/schema/ResolverTest.java +++ b/src/test/java/io/vertx/json/schema/ResolverTest.java @@ -166,11 +166,13 @@ public void testResolveCircularRefsDoWork(Vertx vertx) { // Canary OutputUnit result = repo.validator(schema).validate(fixture); assertThat(result.getValid()).isTrue(); + assertThat(result.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); // Real test (given that the resolved holds the dereferenced metadata, it works as it picks the dereferneced schema // from the __absolute_uri__ field OutputUnit result2 = repo.validator(schema2).validate(fixture); assertThat(result2.getValid()).isTrue(); + assertThat(result.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test diff --git a/src/test/java/io/vertx/json/schema/SchemaDefinitionValidationTest.java b/src/test/java/io/vertx/json/schema/SchemaDefinitionValidationTest.java index 1ea5913e..0378ab56 100644 --- a/src/test/java/io/vertx/json/schema/SchemaDefinitionValidationTest.java +++ b/src/test/java/io/vertx/json/schema/SchemaDefinitionValidationTest.java @@ -46,5 +46,6 @@ void testSchemaDefinitionValidation(Draft draft, Path schemaPath, Vertx vertx) t OutputUnit res = repo.validator(draft.getIdentifier()).validate(schemaToValidate); Assertions.assertTrue(res.getValid()); + Assertions.assertEquals(OutputErrorType.NONE, res.getErrorType()); } } diff --git a/src/test/java/io/vertx/json/schema/ValidationTest.java b/src/test/java/io/vertx/json/schema/ValidationTest.java index bc2ab815..220a61f7 100644 --- a/src/test/java/io/vertx/json/schema/ValidationTest.java +++ b/src/test/java/io/vertx/json/schema/ValidationTest.java @@ -12,7 +12,8 @@ @ExtendWith(VertxExtension.class) class ValidationTest { - private final static JsonSchemaOptions SCHEMA_OPTIONS = new JsonSchemaOptions().setDraft(Draft.DRAFT202012).setBaseUri("app://"); + private final static JsonSchemaOptions SCHEMA_OPTIONS = new JsonSchemaOptions() + .setDraft(Draft.DRAFT202012).setBaseUri("app://").setOutputFormat(OutputFormat.Basic); @Test public void testValidate202012RelyingOnDynamicAnchorDynamicRefShouldFail(Vertx vertx) { @@ -35,6 +36,7 @@ public void testValidate202012RelyingOnDynamicAnchorDynamicRefShouldFail(Vertx v "}")); assertThat(ou.getValid()).isFalse(); + assertThat(ou.getErrorType()).isEqualByComparingTo(OutputErrorType.INVALID_VALUE); } @Test @@ -55,6 +57,7 @@ public void testValidate202012RelyingOnDynamicAnchorDynamicRefShouldPass(Vertx v "}")); assertThat(ou.getValid()).isTrue(); + assertThat(ou.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -70,6 +73,7 @@ public void testValidHostWithNumbers() { assertThat(res.getValid()).isTrue(); assertThat(res.getErrors()).isNull(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -87,7 +91,8 @@ public void testComplexSchema() throws JsonSchemaValidationException { res.checkValidity(); fail("Should have thrown an exception"); } catch (JsonSchemaValidationException e) { - // OK + assertThat(e.errorType()).isEqualByComparingTo(res.getErrorType()); + assertThat(e.errorType()).isEqualByComparingTo(OutputErrorType.MISSING_VALUE); } } @@ -104,6 +109,7 @@ public void testComplexSchema2() throws JsonSchemaValidationException { // Should be fine! res.checkValidity(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -161,8 +167,10 @@ public void testIssue49() throws JsonSchemaValidationException { .put("prop1", "123e4567-e89b-42d3-a456-556642440000") .put("prop2", 42); + OutputUnit res = repository.validator("schema-parent.json").validate(json); + res.checkValidity(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); - repository.validator("schema-parent.json").validate(json).checkValidity(); } @Test @@ -222,6 +230,8 @@ public void testIssue49b() throws JsonSchemaValidationException { .put("prop2", 42); - repository.validator("schema-parent.json").validate(json).checkValidity(); + OutputUnit res = repository.validator("schema-parent.json").validate(json); + res.checkValidity(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } } diff --git a/src/test/java/io/vertx/json/schema/ValidatorTest.java b/src/test/java/io/vertx/json/schema/ValidatorTest.java index 2ffe830d..d06889e2 100644 --- a/src/test/java/io/vertx/json/schema/ValidatorTest.java +++ b/src/test/java/io/vertx/json/schema/ValidatorTest.java @@ -114,6 +114,7 @@ public void testValidateAllArrayEntriesWithNestedErrors() { assertThat(res.getValid()).isFalse(); assertThat(res.getErrors().size()).isEqualTo(4); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.MISSING_VALUE); } @Test @@ -143,6 +144,7 @@ public void testValidateAllObjectPropertiesWithNestedErrors() { assertThat(res.getValid()).isFalse(); assertThat(res.getErrors().size()).isEqualTo(4); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.INVALID_VALUE); } @Test @@ -313,6 +315,7 @@ public void testRecursive() { assertThat(res.getValid()).isTrue(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -485,6 +488,7 @@ public void testQuotedProps() { new JsonObject("{\"definitions\":{\"foo\":{\"type\":\"integer\"}}}")); assertThat(res.getValid()).isTrue(); + assertThat(res.getErrorType()).isEqualByComparingTo(OutputErrorType.NONE); } @Test @@ -507,7 +511,8 @@ public void testValidatorByRef() { .create( new JsonSchemaOptions() .setBaseUri("https://vertx.io") - .setDraft(Draft.DRAFT201909)); + .setDraft(Draft.DRAFT201909) + .setOutputFormat(OutputFormat.Basic)); repository .dereference(JsonSchema.of( @@ -523,8 +528,12 @@ public void testValidatorByRef() { assertThat(validator.validate(true).getValid()) .isEqualTo(true); + assertThat(validator.validate(true).getErrorType()) + .isEqualTo(OutputErrorType.NONE); assertThat(validator.validate("hello world").getValid()) .isEqualTo(false); + assertThat(validator.validate("hello world").getErrorType()) + .isEqualTo(OutputErrorType.INVALID_VALUE); } @Test