diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java index 249215f9a..cfb9879bc 100644 --- a/src/main/java/com/networknt/schema/OneOfValidator.java +++ b/src/main/java/com/networknt/schema/OneOfValidator.java @@ -21,112 +21,24 @@ import org.slf4j.LoggerFactory; import java.util.*; -import java.util.stream.Collectors; public class OneOfValidator extends BaseJsonValidator { private static final Logger logger = LoggerFactory.getLogger(OneOfValidator.class); - private final List schemas = new ArrayList(); - - private static class ShortcutValidator { - private final JsonSchema schema; - private final Map constants; - - ShortcutValidator(JsonNode schemaNode, JsonSchema parentSchema, - ValidationContext validationContext, JsonSchema schema) { - JsonNode refNode = schemaNode.get(ValidatorTypeCode.REF.getValue()); - JsonSchema resolvedRefSchema = refNode != null && refNode.isTextual() ? RefValidator.getRefSchema(parentSchema, validationContext, refNode.textValue()).getSchema() : null; - this.constants = extractConstants(schemaNode, resolvedRefSchema); - this.schema = schema; - } - - private Map extractConstants(JsonNode schemaNode, JsonSchema resolvedRefSchema) { - Map refMap = resolvedRefSchema != null ? extractConstants(resolvedRefSchema.getSchemaNode()) : Collections.emptyMap(); - Map schemaMap = extractConstants(schemaNode); - if (refMap.isEmpty()) { - return schemaMap; - } - if (schemaMap.isEmpty()) { - return refMap; - } - Map joined = new HashMap(); - joined.putAll(schemaMap); - joined.putAll(refMap); - return joined; - } - - private Map extractConstants(JsonNode schemaNode) { - Map result = new HashMap(); - if (!schemaNode.isObject()) { - return result; - } - - JsonNode propertiesNode = schemaNode.get("properties"); - if (propertiesNode == null || !propertiesNode.isObject()) { - return result; - } - Iterator fit = propertiesNode.fieldNames(); - while (fit.hasNext()) { - String fieldName = fit.next(); - JsonNode jsonNode = propertiesNode.get(fieldName); - String constantFieldValue = getConstantFieldValue(jsonNode); - if (constantFieldValue != null && !constantFieldValue.isEmpty()) { - result.put(fieldName, constantFieldValue); - } - } - return result; - } - - private String getConstantFieldValue(JsonNode jsonNode) { - if (jsonNode == null || !jsonNode.isObject() || !jsonNode.has("enum")) { - return null; - } - JsonNode enumNode = jsonNode.get("enum"); - if (enumNode == null || !enumNode.isArray()) { - return null; - } - if (enumNode.size() != 1) { - return null; - } - JsonNode valueNode = enumNode.get(0); - if (valueNode == null || !valueNode.isTextual()) { - return null; - } - return valueNode.textValue(); - } - - public boolean allConstantsMatch(JsonNode node) { - for (Map.Entry e : constants.entrySet()) { - JsonNode valueNode = node.get(e.getKey()); - if (valueNode != null && valueNode.isTextual()) { - boolean match = e.getValue().equals(valueNode.textValue()); - if (!match) { - return false; - } - } - } - return true; - } - - private JsonSchema getSchema() { - return schema; - } - - } + private final List schemas = new ArrayList<>(); public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ONE_OF, validationContext); int size = schemaNode.size(); for (int i = 0; i < size; i++) { JsonNode childNode = schemaNode.get(i); - JsonSchema childSchema = new JsonSchema(validationContext, schemaPath + "/" + i, parentSchema.getCurrentUri(), childNode, parentSchema); - schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema)); + schemas.add(new JsonSchema(validationContext, schemaPath + "/" + i, parentSchema.getCurrentUri(), childNode, parentSchema)); } parseErrorCode(getValidatorType().getErrorCodeKey()); } public Set validate(JsonNode node, JsonNode rootNode, String at) { - Set errors = new LinkedHashSet(); + Set errors = new LinkedHashSet<>(); // As oneOf might contain multiple schemas take a backup of evaluatedProperties. Set backupEvaluatedProperties = CollectorContext.getInstance().copyEvaluatedProperties(); @@ -143,28 +55,13 @@ public Set validate(JsonNode node, JsonNode rootNode, String state.setComplexValidator(true); int numberOfValidSchema = 0; - Set childErrors = new LinkedHashSet(); - // validate that only a single element has been received in the oneOf node - // validation should not continue, as it contradicts the oneOf requirement of only one -// if(node.isObject() && node.size()>1) { -// errors = Collections.singleton(buildValidationMessage(at, "")); -// return Collections.unmodifiableSet(errors); -// } + Set childErrors = new LinkedHashSet<>(); - for (ShortcutValidator validator : schemas) { + for (JsonSchema schema : schemas) { Set schemaErrors = null; // Reset state in case the previous validator did not match state.setMatchedNode(true); - //This prevents from collecting all the error messages in proper format. - /* if (!validator.allConstantsMatch(node)) { - // take a shortcut: if there is any constant that does not match, - // we can bail out of the validation - continue; - }*/ - - // get the current validator - JsonSchema schema = validator.schema; if (!state.isWalkEnabled()) { schemaErrors = schema.validate(node, rootNode, at); } else { @@ -188,37 +85,15 @@ public Set validate(JsonNode node, JsonNode rootNode, String childErrors.addAll(schemaErrors); } - Set childNotRequiredErrors = childErrors.stream().filter(error -> !ValidatorTypeCode.REQUIRED.getValue().equals(error.getType())).collect(Collectors.toSet()); // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1. - if (numberOfValidSchema > 1) { - final ValidationMessage message = getMultiSchemasValidErrorMsg(at); + if (numberOfValidSchema != 1) { + ValidationMessage message = buildValidationMessage(at, Integer.toString(numberOfValidSchema)); if (failFast) { throw new JsonSchemaException(message); } errors.add(message); - } - - // ensure there is always an "OneOf" error reported if number of valid schemas is not equal to 1. - else if (numberOfValidSchema < 1) { - if (!childNotRequiredErrors.isEmpty()) { - childErrors = childNotRequiredErrors; - } - if (!childErrors.isEmpty()) { - if (childErrors.size() > 1) { - Set notAdditionalPropertiesOnly = new LinkedHashSet<>(childErrors.stream() - .filter((ValidationMessage validationMessage) -> !ValidatorTypeCode.ADDITIONAL_PROPERTIES.getValue().equals(validationMessage.getType())) - .sorted((vm1, vm2) -> compareValidationMessages(vm1, vm2)) - .collect(Collectors.toList())); - if (notAdditionalPropertiesOnly.size() > 0) { - childErrors = notAdditionalPropertiesOnly; - } - } - errors.addAll(childErrors); - } - if (failFast) { - throw new JsonSchemaException(errors.toString()); - } + errors.addAll(childErrors); } // Make sure to signal parent handlers we matched @@ -238,85 +113,29 @@ else if (numberOfValidSchema < 1) { } } - /** - * Sort ValidationMessage by its type - * @return - */ - private static int compareValidationMessages(ValidationMessage vm1, ValidationMessage vm2) { - // ValidationMessage's type has smaller index in the list below has high priority - final List typeCodes = Arrays.asList( - ValidatorTypeCode.TYPE.getValue(), - ValidatorTypeCode.DATETIME.getValue(), - ValidatorTypeCode.UUID.getValue(), - ValidatorTypeCode.ID.getValue(), - ValidatorTypeCode.EXCLUSIVE_MAXIMUM.getValue(), - ValidatorTypeCode.EXCLUSIVE_MINIMUM.getValue(), - ValidatorTypeCode.TRUE.getValue(), - ValidatorTypeCode.FALSE.getValue(), - ValidatorTypeCode.CONST.getValue(), - ValidatorTypeCode.CONTAINS.getValue(), - ValidatorTypeCode.PROPERTYNAMES.getValue() - ); - - final int index1 = typeCodes.indexOf(vm1.getType()); - final int index2 = typeCodes.indexOf(vm2.getType()); - - if (index1 >= 0) { - if (index2 >= 0) { - return Integer.compare(index1, index2); - } else { - return -1; - } - } else { - if (index2 >= 0) { - return 1; - } else { - return vm1.getCode().compareTo(vm2.getCode()); - } - } - } - private void resetValidatorState() { ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY); state.setComplexValidator(false); state.setMatchedNode(true); } - public List getChildSchemas() { - List childJsonSchemas = new ArrayList(); - for (ShortcutValidator shortcutValidator: schemas ) { - childJsonSchemas.add(shortcutValidator.getSchema()); - } - return childJsonSchemas; - } - @Override public Set walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) { HashSet validationMessages = new LinkedHashSet(); if (shouldValidateSchema) { validationMessages.addAll(validate(node, rootNode, at)); } else { - for (ShortcutValidator validator : schemas) { - validator.schema.walk(node, rootNode, at , shouldValidateSchema); + for (JsonSchema schema : schemas) { + schema.walk(node, rootNode, at, shouldValidateSchema); } } return validationMessages; } - private ValidationMessage getMultiSchemasValidErrorMsg(String at){ - String msg=""; - for(ShortcutValidator schema: schemas){ - String schemaValue = schema.getSchema().getSchemaNode().toString(); - msg = msg.concat(schemaValue); - } - - return ValidationMessage.of(getValidatorType().getValue(), ValidatorTypeCode.ONE_OF, at, schemaPath, msg); - } - @Override public void preloadJsonSchema() { - for (final ShortcutValidator scValidator: schemas) { - scValidator.getSchema().initializeValidators(); + for (JsonSchema schema: schemas) { + schema.initializeValidators(); } } } diff --git a/src/main/resources/jsv-messages.properties b/src/main/resources/jsv-messages.properties index d1664383f..c12e6fd6b 100644 --- a/src/main/resources/jsv-messages.properties +++ b/src/main/resources/jsv-messages.properties @@ -33,7 +33,7 @@ minimum = {0}: must have a minimum value of {1} multipleOf = {0}: must be multiple of {1} not = {0}: should not be valid to the schema {1} notAllowed = {0}.{1}: is not allowed but it is in the data -oneOf = {0}: should be valid to one and only one of schema, but more than one are valid: {1} +oneOf = {0}: should be valid to one and only one schema, but {1} are valid pattern = {0}: does not match the regex pattern {1} patternProperties = {0}: has some error with 'pattern properties' prefixItems = {0}[{1}]: no validator found at this index diff --git a/src/main/resources/jsv-messages_de.properties b/src/main/resources/jsv-messages_de.properties index e3affd437..610a2bb19 100644 --- a/src/main/resources/jsv-messages_de.properties +++ b/src/main/resources/jsv-messages_de.properties @@ -28,7 +28,7 @@ minimum = {0} muss mindestens den Wert {1} haben multipleOf = {0} muss ein Vielfaches von {1} sein not = {0} darf nicht g�ltig sein f�r das Schema {1} notAllowed = {0}.{1} ist nicht erlaubt und darf folglich nicht auftreten -oneOf = {0} darf nur f�r ein einziges Schema g�ltig sein, aber mehr als ein Schema ist g�ltig: {1} +oneOf = {0} sollte f�r genau ein Schema g�ltig sein, aber {1} sind g�ltig pattern = {0} stimmt nicht mit dem regul�ren Ausdruck {1} �berein patternProperties = {0} stimmt nicht �berein mit dem Format definiert in 'pattern properties' properties = {0}: Ein Fehler mit 'properties' ist aufgetreten diff --git a/src/main/resources/jsv-messages_fa_IR.properties b/src/main/resources/jsv-messages_fa_IR.properties index 89de78682..75b5751aa 100644 --- a/src/main/resources/jsv-messages_fa_IR.properties +++ b/src/main/resources/jsv-messages_fa_IR.properties @@ -28,7 +28,8 @@ minimum = {0}: باید حداقل {1} ویژگی داشته باشد multipleOf = {0}: باید مضرب از {1} باشد not = {0}: نباید برای طرحواره معتبر باشد {1} notAllowed = {0}.{1}: مجاز نیست اما در داده ها وجود دارد -oneOf = {0}: باید برای یک و تنها یکی از طرحواره ها معتبر باشد، اما بیش از یکی معتبر است: {1} +# needs to be re-worked by a native speaker +#oneOf = {0}: باید برای یک و تنها یکی از طرحواره ها معتبر باشد، اما بیش از یکی معتبر است: {1} pattern = {0}: با الگوی regex مطابقت ندارد {1} patternProperties = {0}: دارای مقداری خطا با 'خواص الگو' prefixItems = {0}[{1}]: هیچ اعتبارسنجی در این فهرست یافت نشد diff --git a/src/main/resources/jsv-messages_fr.properties b/src/main/resources/jsv-messages_fr.properties index 8fbf704f6..b9bc8807a 100644 --- a/src/main/resources/jsv-messages_fr.properties +++ b/src/main/resources/jsv-messages_fr.properties @@ -28,7 +28,7 @@ minimum = {0}: doit avoir une valeur minimale de {1} multipleOf = {0}: doit �tre un multiple de {1} not = {0}: ne doit pas �tre valide pour le sch�ma {1} notAllowed = {0}.{1} n'est pas autoris� mais est dans les donn�es -oneOf = {0}: devrait �tre valide pour un et un seul des sch�mas, mais plus d'un sont valides : {1} +oneOf = {0}: doit �tre valide pour un et un seul sch�ma, mais {1} sont valides pattern = {0} ne correspond pas � l'expression r�guli�re {1} patternProperties = {0}: a des erreurs avec 'pattern properties' properties = {0} : a une erreur avec 'properties' diff --git a/src/main/resources/jsv-messages_it.properties b/src/main/resources/jsv-messages_it.properties index e004b5115..6a8bf84d2 100644 --- a/src/main/resources/jsv-messages_it.properties +++ b/src/main/resources/jsv-messages_it.properties @@ -28,7 +28,7 @@ minimum={0}: deve avere un valore minimo di {1} multipleOf={0}: deve essere un multiplo di {1} not={0}: non dovrebbe essere valido per lo schema {1} notAllowed={0}.{1}: non � consentito ma � nel dato -oneOf={0}: dovrebbe essere valido a uno e solo uno schema, ma pi� di uno sono validi: {1} +oneOf={0}: dovrebbe essere valido per uno e un solo schema, ma {1} sono validi pattern={0}: non corrisponde alla regex {1} patternProperties={0}: ha qualche errore con ''pattern properties'' prefixItems={0}[{1}]: nessun validatore trovato a quest''indice diff --git a/src/test/java/com/networknt/schema/AdditionalPropertiesOneOfFailsTest.java b/src/test/java/com/networknt/schema/AdditionalPropertiesOneOfFailsTest.java deleted file mode 100644 index 472aa28f1..000000000 --- a/src/test/java/com/networknt/schema/AdditionalPropertiesOneOfFailsTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2020 Network New Technologies Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.networknt.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.io.InputStream; -import java.util.Set; - -public class AdditionalPropertiesOneOfFailsTest { - - private static Set errors = null; - - protected JsonSchema getJsonSchemaFromStreamContent(InputStream schemaContent) { - JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - return factory.getSchema(schemaContent); - } - - protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception { - ObjectMapper mapper = new ObjectMapper(); - JsonNode node = mapper.readTree(content); - return node; - } - - @BeforeEach - public void withJsonSchema() { - // correct processing would include the assertions in the tests - if (errors == null) { - String schemaPathJson = "/openapi3/AdditionalPropertiesOneOfFailsTest.json"; - String dataPath = "/data/AdditionalPropertiesOneOfFailsTest.json"; - InputStream schemaInputStream = getClass().getResourceAsStream(schemaPathJson); - - JsonSchema schema = getJsonSchemaFromStreamContent(schemaInputStream); - schema.getValidationContext().getConfig().setFailFast(false); - - - InputStream dataInputStream = getClass().getResourceAsStream(dataPath); - try { - JsonNode node = getJsonNodeFromStreamContent(dataInputStream); - - errors = schema.validate(node); - - System.out.println("nr. of reported errors: " + errors.size()); - errors.stream().forEach(er -> System.out.println(er.toString())); - } catch (Exception e) { - System.out.println("Fail!"); - } - - } - } - - @Test - @Disabled - public void toxicIsAdditional() { - Assertions.assertTrue(errors.stream().filter(er -> er.toString().contains("toxic: is not defined in the schema")).count() == 2, - "property toxic is not defined on activity chemical"); - } - - @Test - @Disabled - public void chemicalCharacteristicNameIsAdditional() { - - - Assertions.assertTrue(errors.stream().filter(er -> er.toString().contains("$.activities[2].chemicalCharacteristic.name: is not defined in the schema")).count() == 1, - "property name is not defined in 'oneOf' the ChemicalCharacteristic component schemas"); - } - - - @Test - @Disabled - public void depthIsAdditional() { - - Assertions.assertTrue(errors.stream().filter(er -> er.toString().contains("depth: is not defined in the schema")).count() == 1, - "property depth is not defined on activity machine"); - } - - @Test - @Disabled - public void chemicalCharacteristicCategoryNameIsDefined() { - - Assertions.assertFalse(errors.stream().filter(er -> er.toString().contains("$.activities[0].chemicalCharacteristic.categoryName: is not defined in the schema")).count() == 1, - "property categoryName is defined in 'oneOf' the ChemicalCharacteristic component schemas "); - } - - @Test - @Disabled - public void weightIsMissingOnlyOnce() { - - Assertions.assertTrue(errors.stream().filter(er -> er.toString().contains("weight: is missing")).count() == 1, - "property weight is required on activity machine "); - } - - @Test - @Disabled - public void heightIsNotMissingNotOnceAndNotTwice() { - - Assertions.assertFalse(errors.stream().filter(er -> er.toString().contains("heigth: is missing")).count() == 1, - "property height is defined "); - - } - - @Test - @Disabled - public void heightWrongType() { - - Assertions.assertTrue(errors.stream().filter(er -> er.toString().contains("heigth: number found, integer expected")).count() == 1, - "property height has the wrong type"); - - } -} diff --git a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java index 3c0d27fae..04773e794 100644 --- a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java +++ b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java @@ -96,7 +96,7 @@ public void neitherValid() throws Exception { JsonNode dataNode = testNode.get("data"); Set errors = jsonSchema.validate(dataNode); assertTrue(!errors.isEmpty()); - assertEquals(errors.size(),2); + assertEquals(errors.size(),3); } private URI getSchema() { diff --git a/src/test/java/com/networknt/schema/Issue425Test.java b/src/test/java/com/networknt/schema/Issue425Test.java deleted file mode 100644 index 6afa40e97..000000000 --- a/src/test/java/com/networknt/schema/Issue425Test.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.networknt.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import org.junit.jupiter.api.Test; - -import java.io.InputStream; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -public class Issue425Test extends HTTPServiceSupport { - protected ObjectMapper mapper = new ObjectMapper(); - protected JsonSchemaFactory validatorFactory = JsonSchemaFactory - .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).objectMapper(mapper).build(); - - private void runTestFile(String testCaseFile) throws Exception { - final URI testCaseFileUri = URI.create("classpath:" + testCaseFile); - InputStream in = Thread.currentThread().getContextClassLoader() - .getResourceAsStream(testCaseFile); - ArrayNode testCases = mapper.readValue(in, ArrayNode.class); - - for (int j = 0; j < testCases.size(); j++) { - try { - JsonNode testCase = testCases.get(j); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - - ArrayNode testNodes = (ArrayNode) testCase.get("tests"); - for (int i = 0; i < testNodes.size(); i++) { - JsonNode test = testNodes.get(i); - System.out.println("=== " + test.get("description")); - JsonNode node = test.get("data"); - JsonNode typeLooseNode = test.get("isTypeLoose"); - // Configure the schemaValidator to set typeLoose's value based on the test file, - // if test file do not contains typeLoose flag, use default value: true. - config.setTypeLoose(typeLooseNode != null && typeLooseNode.asBoolean()); - config.setOpenAPI3StyleDiscriminators(false); - JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), config); - - List errors = new ArrayList(schema.validate(node)); - - if (test.get("valid").asBoolean()) { - if (!errors.isEmpty()) { - System.out.println("---- test case failed ----"); - System.out.println("schema: " + schema.toString()); - System.out.println("data: " + test.get("data")); - System.out.println("errors:"); - for (ValidationMessage error : errors) { - System.out.println(error); - } - } - if(test.get("data").get("values").asText().equals("3")) - assertEquals(2, errors.size()); - } else { - if (errors.isEmpty()) { - System.out.println("---- test case failed ----"); - System.out.println("schema: " + schema); - System.out.println("data: " + test.get("data")); - } else { - JsonNode errorCount = test.get("errorCount"); - if (errorCount != null && errorCount.isInt() && errors.size() != errorCount.asInt()) { - System.out.println("---- test case failed ----"); - System.out.println("schema: " + schema); - System.out.println("data: " + test.get("data")); - System.out.println("errors: " + errors); - for (ValidationMessage error : errors) { - System.out.println(error); - } - assertEquals(errorCount.asInt(), errors.size(), "expected error count"); - } - } - assertFalse(errors.isEmpty()); - } - - } - - - } catch (JsonSchemaException e) { - throw new IllegalStateException(String.format("Current schema should not be invalid: %s", testCaseFile), e); - } - } - } - - @Test - public void testNullableOneOf() throws Exception { - runTestFile("data/issue425.json"); - } -} diff --git a/src/test/java/com/networknt/schema/Issue470Test.java b/src/test/java/com/networknt/schema/Issue470Test.java deleted file mode 100644 index a9bbdd9b4..000000000 --- a/src/test/java/com/networknt/schema/Issue470Test.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.networknt.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.io.InputStream; -import java.util.Set; - -public class Issue470Test { - - private static JsonSchema schema; - - @BeforeAll - static void init() { - JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - String schemaPath = "/schema/issue470-v7.json"; - InputStream schemaInputStream = Issue470Test.class.getResourceAsStream(schemaPath); - schema = factory.getSchema(schemaInputStream); - } - - private JsonNode getJsonNodeFromJsonData(String jsonFilePath) throws Exception { - InputStream content = getClass().getResourceAsStream(jsonFilePath); - ObjectMapper mapper = new ObjectMapper(); - return mapper.readTree(content); - } - - @Test - @DisplayName("Test valid oneOf option 1") - public void testValidJson1() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-valid-1.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 2") - public void testValidJson2() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-valid-2.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test invalid oneOf option 1 - wrong type") - public void testInvalidJson1() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-invalid-1.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.byName.name: integer found, string expected", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 1 - invalid value") - public void testInvalidJson2() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-invalid-2.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.byName.name: may only be 20 characters long", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 2 - wrong type") - public void testInvalidJson3() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-invalid-3.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.byAge.age: string found, integer expected", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 2 - invalid value") - public void testInvalidJson4() throws Exception { - JsonNode node = getJsonNodeFromJsonData("/data/issue470-invalid-4.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.byAge.age: must have a maximum value of 150", errors.iterator().next().getMessage()); - } -} diff --git a/src/test/java/com/networknt/schema/Issue491Test.java b/src/test/java/com/networknt/schema/Issue491Test.java deleted file mode 100644 index 777655001..000000000 --- a/src/test/java/com/networknt/schema/Issue491Test.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.networknt.schema; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.io.InputStream; -import java.util.Set; -import java.util.stream.Stream; - -class Issue491Test { - - private static JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - private static String schemaPath1 = "/schema/issue491-v7.json"; - private static String schemaPath2 = "/schema/issue491_2-v7.json"; - private static String schemaPath3 = "/schema/issue491_3-v7.json"; - - private JsonNode getJsonNodeFromJsonData(String jsonFilePath) throws Exception { - InputStream content = getClass().getResourceAsStream(jsonFilePath); - ObjectMapper mapper = new ObjectMapper(); - return mapper.readTree(content); - } - - @Test - @DisplayName("Test valid oneOf option 1") - void testValidJson1() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-1.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 2") - void testValidJson2() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 1") - void testValidJson3() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-3.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 2") - void testValidJson4() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 1") - void testValidJson5() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-4.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test valid oneOf option 2") - void testValidJson6() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json"); - Set errors = schema.validate(node); - Assertions.assertTrue(errors.isEmpty()); - } - - @Test - @DisplayName("Test invalid oneOf option 1 - wrong type") - void testInvalidJson1() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-1.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.searchAge.age: string found, integer expected", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 2 - wrong type") - void testInvalidJson2() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-2.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.name: integer found, string expected", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 1 - wrong type") - void testInvalidJson3() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-3.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.byAge.age: string found, integer expected", errors.iterator().next().getMessage()); - } - - @Test - @DisplayName("Test invalid oneOf option 2 - wrong type") - void testInvalidJson4() throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-2.json"); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals("$.search.name: integer found, string expected", errors.iterator().next().getMessage()); - } - - @ParameterizedTest - @MethodSource("parametersProvider") - @DisplayName("Test invalid oneOf option - wrong types or values") - void testInvalidJson5(String jsonPath, String expectedError) throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3); - JsonSchema schema = factory.getSchema(schemaInputStream); - JsonNode node = getJsonNodeFromJsonData(jsonPath); - Set errors = schema.validate(node); - Assertions.assertEquals(1, errors.size()); - Assertions.assertEquals(expectedError, errors.iterator().next().getMessage()); - } - - private static Stream parametersProvider() { - return Stream.of( - Arguments.of("/data/issue491-invalid-4.json", "$.search.age: string found, integer expected"), - Arguments.of("/data/issue491-invalid-2.json", "$.search.name: integer found, string expected"), - Arguments.of("/data/issue491-invalid-5.json", "$.search.age: must have a maximum value of 150"), - Arguments.of("/data/issue491-invalid-6.json", "$.search.name: may only be 20 characters long") - ); - } -} diff --git a/src/test/java/com/networknt/schema/Issue493Test.java b/src/test/java/com/networknt/schema/Issue493Test.java index 6bc65d6cf..07ba12bf1 100644 --- a/src/test/java/com/networknt/schema/Issue493Test.java +++ b/src/test/java/com/networknt/schema/Issue493Test.java @@ -57,7 +57,7 @@ void testValidJson2 () void testInvalidJson1 () throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); + InputStream schemaInputStream = Issue493Test.class.getResourceAsStream(schemaPath1); JsonSchema schema = factory.getSchema(schemaInputStream); JsonNode node = getJsonNodeFromJsonData("/data/issue493-invalid-1.json"); Set errors = schema.validate(node); @@ -77,18 +77,20 @@ void testInvalidJson1 () void testInvalidJson2 () throws Exception { - InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1); + InputStream schemaInputStream = Issue493Test.class.getResourceAsStream(schemaPath1); JsonSchema schema = factory.getSchema(schemaInputStream); JsonNode node = getJsonNodeFromJsonData("/data/issue493-invalid-2.json"); Set errors = schema.validate(node); - Assertions.assertEquals(2, errors.size()); + Assertions.assertEquals(3, errors.size()); Set allErrorMessages = new HashSet<>(); errors.forEach(vm -> { allErrorMessages.add(vm.getMessage()); }); - assertThat(allErrorMessages, - Matchers.containsInAnyOrder("$.parameters[1].value: string found, integer expected", - "$.parameters[1].value: does not match the regex pattern ^\\{\\{.+\\}\\}$")); + assertThat(allErrorMessages, Matchers.containsInAnyOrder( + "$.parameters[1].value: string found, integer expected", + "$.parameters[1].value: does not match the regex pattern ^\\{\\{.+\\}\\}$", + "$.parameters[1]: should be valid to one and only one schema, but 0 are valid" + )); } } diff --git a/src/test/java/com/networknt/schema/Issue664Test.java b/src/test/java/com/networknt/schema/Issue664Test.java index 813fd743f..33fd98095 100644 --- a/src/test/java/com/networknt/schema/Issue664Test.java +++ b/src/test/java/com/networknt/schema/Issue664Test.java @@ -34,9 +34,11 @@ void shouldHaveFullSchemaPaths() throws Exception { List errorSchemaPaths = schema.validate(node).stream().map(ValidationMessage::getSchemaPath).collect(Collectors.toList()); List expectedSchemaPaths = Arrays.asList( - "#/items/allOf/0/anyOf/0/oneOf/0/not", - "#/items/allOf/1/else/properties/postal_code/pattern", - "#/items/allOf/1/then/properties/postal_code/pattern"); + "#/items/allOf/0/anyOf/0/oneOf", + "#/items/allOf/0/anyOf/0/oneOf/0/not", + "#/items/allOf/1/else/properties/postal_code/pattern", + "#/items/allOf/1/then/properties/postal_code/pattern" + ); MatcherAssert.assertThat(errorSchemaPaths, Matchers.containsInAnyOrder(expectedSchemaPaths.toArray())); } } diff --git a/src/test/resources/data/AdditionalPropertiesOneOfFailsTest.json b/src/test/resources/data/AdditionalPropertiesOneOfFailsTest.json deleted file mode 100644 index c0a36ee44..000000000 --- a/src/test/resources/data/AdditionalPropertiesOneOfFailsTest.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "locationName": "factoryLocation", - "activities": [ - { - "activityType": "machine", - "age": "(additionalProperty not allowed)", - "height": 10.5 - }, - { - "activityType": "chemical", - "toxic": "(additionalProperty not allowed)", - "chemicalCharacteristic": { - "commonName": "methane", - "chemicalName": "CH4" - } - }, - { - "activityType": "chemical", - "toxic": "(additionalProperty not allowed)", - "chemicalCharacteristic": { - "name": "methane", - "categoryName": "gasses", - "chemicalName": "CH4" - } - } - ] -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-invalid-1.json b/src/test/resources/data/issue470-invalid-1.json deleted file mode 100644 index 0afba7a06..000000000 --- a/src/test/resources/data/issue470-invalid-1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byName": { - "name": 123 - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-invalid-2.json b/src/test/resources/data/issue470-invalid-2.json deleted file mode 100644 index 65ea67661..000000000 --- a/src/test/resources/data/issue470-invalid-2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byName": { - "name": "Too loooooooooong name" - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-invalid-3.json b/src/test/resources/data/issue470-invalid-3.json deleted file mode 100644 index 2c81c3a17..000000000 --- a/src/test/resources/data/issue470-invalid-3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byAge": { - "age": "20" - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-invalid-4.json b/src/test/resources/data/issue470-invalid-4.json deleted file mode 100644 index 6a0d1b6f3..000000000 --- a/src/test/resources/data/issue470-invalid-4.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byAge": { - "age": 200 - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-valid-1.json b/src/test/resources/data/issue470-valid-1.json deleted file mode 100644 index 04b65d967..000000000 --- a/src/test/resources/data/issue470-valid-1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byName": { - "name": "John" - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue470-valid-2.json b/src/test/resources/data/issue470-valid-2.json deleted file mode 100644 index 1fb9e8477..000000000 --- a/src/test/resources/data/issue470-valid-2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byAge": { - "age": 35 - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-1.json b/src/test/resources/data/issue491-invalid-1.json deleted file mode 100644 index 80dd10568..000000000 --- a/src/test/resources/data/issue491-invalid-1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "searchAge": { - "age": "Steve" - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-2.json b/src/test/resources/data/issue491-invalid-2.json deleted file mode 100644 index 29f140c72..000000000 --- a/src/test/resources/data/issue491-invalid-2.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "name": 123 - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-3.json b/src/test/resources/data/issue491-invalid-3.json deleted file mode 100644 index 37878f8db..000000000 --- a/src/test/resources/data/issue491-invalid-3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byAge": { - "age": "Steve" - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-4.json b/src/test/resources/data/issue491-invalid-4.json deleted file mode 100644 index 79c8d4f37..000000000 --- a/src/test/resources/data/issue491-invalid-4.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "age": "Steve" - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-5.json b/src/test/resources/data/issue491-invalid-5.json deleted file mode 100644 index 9528725a7..000000000 --- a/src/test/resources/data/issue491-invalid-5.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "age": 200 - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-invalid-6.json b/src/test/resources/data/issue491-invalid-6.json deleted file mode 100644 index 3fad4f39b..000000000 --- a/src/test/resources/data/issue491-invalid-6.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "name": "TooLoooooooooooooooooooooooooooooooooongName" - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-valid-1.json b/src/test/resources/data/issue491-valid-1.json deleted file mode 100644 index edc86d3a9..000000000 --- a/src/test/resources/data/issue491-valid-1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "searchAge": { - "age": 50 - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-valid-2.json b/src/test/resources/data/issue491-valid-2.json deleted file mode 100644 index d71a75166..000000000 --- a/src/test/resources/data/issue491-valid-2.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "name": "Steve" - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-valid-3.json b/src/test/resources/data/issue491-valid-3.json deleted file mode 100644 index 60d4577d7..000000000 --- a/src/test/resources/data/issue491-valid-3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "search": { - "byAge": { - "age": 50 - } - } -} \ No newline at end of file diff --git a/src/test/resources/data/issue491-valid-4.json b/src/test/resources/data/issue491-valid-4.json deleted file mode 100644 index ac8d2c8d7..000000000 --- a/src/test/resources/data/issue491-valid-4.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "search": { - "age": 50 - } -} \ No newline at end of file diff --git a/src/test/resources/draft2020-12/issue656.json b/src/test/resources/draft2020-12/issue656.json new file mode 100644 index 000000000..65fd78294 --- /dev/null +++ b/src/test/resources/draft2020-12/issue656.json @@ -0,0 +1,163 @@ +[ + { + "description": "issue656 - Should be valid to one and only one of schema, but more than one are valid error", + "schema": { + "$id": "someid", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "JSON Schema for treatments", + "oneOf": [ + { + "$ref": "#/$defs/drug-treatment" + }, + { + "$ref": "#/$defs/surgery-treatment" + } + ], + "$defs": { + "base": { + "type": "object", + "properties": { + "type-tag": { + "enum": [ + "SURGERY", + "DRUGTREATMENT", + "RADIOLOGY", + "PHYSIOTHERAPY" + ] + }, + "id": { + "type": "string", + "format": "uuid" + }, + "patient-id": { + "type": "string", + "format": "uuid" + }, + "patient-name": { + "type": "string" + }, + "provider-id": { + "type": "string", + "format": "uuid" + }, + "provider-name": { + "type": "string" + }, + "diagnosis": { + "type": "string" + }, + "followup-treatments": { + "type": "array", + "items": { + "$ref": "#" + } + } + }, + "required": [ + "id", + "type-tag", + "patient-id", + "patient-name", + "provider-id", + "provider-name", + "diagnosis", + "followup-treatments" + ] + }, + "drug-treatment": { + "allOf": [ + { + "$ref": "#/$defs/base" + } + ], + "properties": { + "drug": { + "type": "string" + }, + "dosage": { + "type": "number" + }, + "start-date": { + "type": "string", + "format": "date" + }, + "end-date": { + "type": "string", + "format": "date" + }, + "frequency": "integer" + }, + "required": [ + "drug", + "dosage", + "start-date", + "end-date", + "frequency" + ], + "unevaluatedProperties": false + }, + "surgery-treatment": { + "allOf": [ + { + "$ref": "#/$defs/base" + } + ], + "properties": { + "surgery-date": { + "type": "string", + "format": "date" + }, + "discharge-instructions": { + "type": "string" + }, + "required": [ + "surgery-date", + "discharge-instructions" + ], + "unevaluatedProperties": false + } + } + } + }, + "tests": [ + { + "description": "Sample 1", + "data": { + "type-tag": "SURGERY", + "surgery-date": "2222-02-12", + "discharge-instructions": "dsfdsfdsfds", + "id": "12d2e565-8966-4029-840b-1959277b37f6", + "patient-id": "ab62420e-0bd8-4e39-8e0b-36e464b7abb2", + "patient-name": "Tom", + "provider-id": "154523b2-7598-4ed4-aab1-b2ef1692109c", + "provider-name": "gdsfdsd", + "diagnosis": "fdsfds", + "followup-treatments": [] + }, + "valid": true + }, + { + "description": "Sample 2", + "data": { + "type-tag": "DRUGTREATMENT", + "drug": "fdsds", + "dosage": 2.0, + "start-date": "2222-02-12", + "end-date": "2222-02-12", + "frequency": 2, + "id": "aa7da984-0252-45b1-b0cd-f1dbe98662e2", + "patient-id": "ab62420e-0bd8-4e39-8e0b-36e464b7abb2", + "patient-name": "Tom", + "provider-id": "154523b2-7598-4ed4-aab1-b2ef1692109c", + "provider-name": "gdsfdsd", + "diagnosis": "sfdsfds", + "followup-treatments": [] + }, + "valid": false, + "validationMessages": [ + "$: should be valid to one and only one schema, but 2 are valid" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/data/issue425.json b/src/test/resources/draft4/issue425.json similarity index 77% rename from src/test/resources/data/issue425.json rename to src/test/resources/draft4/issue425.json index c0e998819..ec205db5a 100644 --- a/src/test/resources/data/issue425.json +++ b/src/test/resources/draft4/issue425.json @@ -33,7 +33,12 @@ "data": { "values": 3 }, - "valid": true + "valid": false, + "validationMessages": [ + "$.values: should be valid to one and only one schema, but 0 are valid", + "$.values: integer found, array expected", + "$.values: integer found, string expected" + ] }, { "description": "oneOf with single string array", diff --git a/src/test/resources/draft7/issue470.json b/src/test/resources/draft7/issue470.json new file mode 100644 index 000000000..09aad528e --- /dev/null +++ b/src/test/resources/draft7/issue470.json @@ -0,0 +1,142 @@ +[ + { + "description": "Issue470Test", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/issue-470.json", + "title": "OneOf validation message", + "description": "Test description", + "type": "object", + "properties": { + "search": { + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { + "byName": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20, + "minLength": 1 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "byAge": { + "type": "object", + "properties": { + "age": { + "type": "integer", + "maximum": 150, + "minimum": 1 + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "Test valid oneOf option 1", + "data": { + "search": { + "byName": { + "name": "John" + } + } + }, + "valid": true + }, + { + "description": "Test valid oneOf option 2", + "data": { + "search": { + "byAge": { + "age": 35 + } + } + }, + "valid": true + }, + { + "description": "Test invalid oneOf option 1 - wrong type", + "data": { + "search": { + "byName": { + "name": 123 + } + } + }, + "valid": false, + "validationMessages": [ + "$.search: should be valid to one and only one schema, but 0 are valid", + "$.search.byName: is not defined in the schema and the schema does not allow additional properties", + "$.search.byName.name: integer found, string expected" + ] + }, + { + "description": "Test invalid oneOf option 1 - invalid value", + "data": { + "search": { + "byName": { + "name": "Too loooooooooong name" + } + } + }, + "valid": false, + "validationMessages": [ + "$.search: should be valid to one and only one schema, but 0 are valid", + "$.search.byName: is not defined in the schema and the schema does not allow additional properties", + "$.search.byName.name: may only be 20 characters long" + ] + }, + { + "description": "Test invalid oneOf option 2 - wrong type", + "data": { + "search": { + "byAge": { + "age": "20" + } + } + }, + "valid": false, + "validationMessages": [ + "$.search: should be valid to one and only one schema, but 0 are valid", + "$.search.byAge.age: string found, integer expected", + "$.search.byAge: is not defined in the schema and the schema does not allow additional properties" + ] + }, + { + "description": "Test invalid oneOf option 2 - invalid value", + "data": { + "search": { + "byAge": { + "age": 200 + } + } + }, + "valid": false, + "validationMessages": [ + "$.search: should be valid to one and only one schema, but 0 are valid", + "$.search.byAge.age: must have a maximum value of 150", + "$.search.byAge: is not defined in the schema and the schema does not allow additional properties" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/draft7/issue491.json b/src/test/resources/draft7/issue491.json new file mode 100644 index 000000000..a15fbf680 --- /dev/null +++ b/src/test/resources/draft7/issue491.json @@ -0,0 +1,328 @@ +[ + { + "description": "issue491 Schema 1", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/issue-470.json", + "title": "OneOf validation message", + "description": "Test description", + "type": "object", + "properties": { + "search": { + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { + "searchAge": { + "type": "object", + "properties": { + "age": { + "type": "integer", + "maximum": 150, + "minimum": 1 + } + }, + "required": [ + "age" + ] + } + }, + "required": [ + "searchAge" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20, + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "Test valid oneOf option 1", + "data": { + "search": { + "searchAge": { + "age": 50 + } + } + }, + "valid": true + }, + { + "description": "Test valid oneOf option 2", + "data": { + "search": { + "name": "Steve" + } + }, + "valid": true + }, + { + "description": "Test invalid oneOf option 1 - wrong type", + "data": { + "search": { + "searchAge": { + "age": "Steve" + } + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: is missing but it is required", + "$.search.searchAge.age: string found, integer expected", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + }, + { + "description": "Test invalid oneOf option 2 - wrong type", + "data": { + "search": { + "name": 123 + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: integer found, string expected", + "$.search.searchAge: is missing but it is required", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + } + ] + }, + { + "description": "issue491 Schema 2", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/issue-470.json", + "title": "OneOf validation message", + "description": "Test description", + "type": "object", + "properties": { + "search": { + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { + "byAge": { + "type": "object", + "properties": { + "age": { + "type": "integer", + "maximum": 150, + "minimum": 1 + } + }, + "required": [ + "age" + ] + } + }, + "required": [ + "byAge" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20, + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "Test valid oneOf option 1", + "data": { + "search": { + "name": "Steve" + } + }, + "valid": true + }, + { + "description": "Test valid oneOf option 2", + "data": { + "search": { + "name": "Steve" + } + }, + "valid": true + }, + { + "description": "Test invalid oneOf option 1 - wrong type", + "data": { + "search": { + "byAge": { + "age": "Steve" + } + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: is missing but it is required", + "$.search.byAge.age: string found, integer expected", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + }, + { + "description": "Test invalid oneOf option 2 - wrong type", + "data": { + "search": { + "name": 123 + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: integer found, string expected", + "$.search.byAge: is missing but it is required", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + } + ] + }, + { + "description": "issue491 Schema 3", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/issue-470.json", + "title": "OneOf validation message", + "description": "Test description", + "type": "object", + "properties": { + "search": { + "type": "object", + "oneOf": [ + { + "type": "object", + "properties": { + "age": { + "type": "integer", + "maximum": 150, + "minimum": 1 + } + }, + "required": [ + "age" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20, + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + ] + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "Test valid oneOf option 1", + "data": { + "search": { + "age": 50 + } + }, + "valid": true + }, + { + "description": "Test valid oneOf option 2", + "data": { + "search": { + "name": "Steve" + } + }, + "valid": true + }, + { + "description": "Test invalid oneOf option - wrong types or values 1", + "data": { + "search": { + "age": "Steve" + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: is missing but it is required", + "$.search.age: string found, integer expected", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + }, + { + "description": "Test invalid oneOf option - wrong types or values 2", + "data": { + "search": { + "name": 123 + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: integer found, string expected", + "$.search.age: is missing but it is required", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + }, + { + "description": "Test invalid oneOf option - wrong types or values 3", + "data": { + "search": { + "age": 200 + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: is missing but it is required", + "$.search.age: must have a maximum value of 150", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + }, + { + "description": "Test invalid oneOf option - wrong types or values 4", + "data": { + "search": { + "name": "TooLoooooooooooooooooooooooooooooooooongName" + } + }, + "valid": false, + "validationMessages": [ + "$.search.name: may only be 20 characters long", + "$.search.age: is missing but it is required", + "$.search: should be valid to one and only one schema, but 0 are valid" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/draft7/issue516.json b/src/test/resources/draft7/issue516.json new file mode 100644 index 000000000..c9c33f299 --- /dev/null +++ b/src/test/resources/draft7/issue516.json @@ -0,0 +1,144 @@ +[ + { + "description": "issue516", + "schema": { + "type": "object", + "properties": { + "locationName": { + "type": "string" + }, + "activities": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "required": [ + "activityType", + "weight", + "height" + ], + "additionalProperties": false, + "properties": { + "activityType": { + "enum": [ + "machine" + ] + }, + "weight": { + "type": "integer" + }, + "height": { + "type": "integer" + } + } + }, + { + "type": "object", + "required": [ + "activityType", + "chemicalCharacteristic" + ], + "additionalProperties": false, + "properties": { + "activityType": { + "enum": [ + "chemical" + ] + }, + "chemicalCharacteristic": { + "oneOf": [ + { + "type": "object", + "required": [ + "chemicalName" + ], + "additionalProperties": false, + "properties": { + "commonName": { + "type": "string" + }, + "chemicalName": { + "type": "string" + } + } + }, + { + "type": "object", + "required": [ + "chemicalName" + ], + "additionalProperties": false, + "properties": { + "categoryName": { + "type": "string" + }, + "chemicalName": { + "type": "string" + } + } + } + ] + } + } + } + ] + } + } + } + }, + "tests": [ + { + "description": "OneOfValidator is filtering out the required errors if all the oneOf schemas are having the issues", + "data": { + "locationName": "factoryLocation", + "activities": [ + { + "activityType": "machine", + "age": "(additionalProperty not allowed)", + "height": 10.5 + }, + { + "activityType": "chemical", + "toxic": "(additionalProperty not allowed)", + "chemicalCharacteristic": { + "commonName": "methane", + "chemicalName": "CH4" + } + }, + { + "activityType": "chemical", + "toxic": "(additionalProperty not allowed)", + "chemicalCharacteristic": { + "name": "methane", + "categoryName": "gasses", + "chemicalName": "CH4" + } + } + ] + }, + "valid": false, + "validationMessages": [ + "$.activities[0]: should be valid to one and only one schema, but 0 are valid", + "$.activities[0].age: is not defined in the schema and the schema does not allow additional properties", + "$.activities[0].chemicalCharacteristic: is missing but it is required", + "$.activities[0].height: is not defined in the schema and the schema does not allow additional properties", + "$.activities[0].weight: is missing but it is required", + "$.activities[1]: should be valid to one and only one schema, but 0 are valid", + "$.activities[1].chemicalCharacteristic: is not defined in the schema and the schema does not allow additional properties", + "$.activities[1].height: is missing but it is required", + "$.activities[1].toxic: is not defined in the schema and the schema does not allow additional properties", + "$.activities[1].weight: is missing but it is required", + "$.activities[2]: should be valid to one and only one schema, but 0 are valid", + "$.activities[2].chemicalCharacteristic: is not defined in the schema and the schema does not allow additional properties", + "$.activities[2].chemicalCharacteristic: should be valid to one and only one schema, but 0 are valid", + "$.activities[2].chemicalCharacteristic.categoryName: is not defined in the schema and the schema does not allow additional properties", + "$.activities[2].chemicalCharacteristic.name: is not defined in the schema and the schema does not allow additional properties", + "$.activities[2].height: is missing but it is required", + "$.activities[2].toxic: is not defined in the schema and the schema does not allow additional properties", + "$.activities[2].weight: is missing but it is required" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/draft7/issue653.json b/src/test/resources/draft7/issue653.json new file mode 100644 index 000000000..c576bf73a --- /dev/null +++ b/src/test/resources/draft7/issue653.json @@ -0,0 +1,90 @@ +[ + { + "description": "issue653", + "schema": { + "title": "PetArray", + "type": "object", + "properties": { + "pets": { + "type": "array", + "items": { + "oneOf": [ + { + "if": { + "properties": { + "pet_type": { + "const": "Cat" + } + } + }, + "then": { + "type": "object", + "properties": { + "hunts": { + "type": "boolean" + }, + "age": { + "type": "integer" + } + }, + "required": [ + "age" + ] + }, + "else": false + }, + { + "if": { + "properties": { + "pet_type": { + "const": "Dog" + } + } + }, + "then": { + "type": "object", + "properties": { + "bark": { + "type": "boolean" + }, + "breed": { + "type": "string" + } + }, + "required": [ + "bark" + ] + }, + "else": false + } + ] + } + } + }, + "additionalProperties": false, + "required": [ + "pets" + ] + }, + "tests": [ + { + "description": "OneOf Validation removes validations, making invalid data appear valid", + "data": { + "pets": [ + { + "pet_type": "Cat", + "hunts": "asdf", + "additionaValue": "asdf" + } + ] + }, + "valid": false, + "validationMessages": [ + "$.pets[0]: should be valid to one and only one schema, but 0 are valid", + "$.pets[0].age: is missing but it is required", + "Boolean schema false is not valid" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/draft7/issue678.json b/src/test/resources/draft7/issue678.json new file mode 100644 index 000000000..b8873ce5c --- /dev/null +++ b/src/test/resources/draft7/issue678.json @@ -0,0 +1,60 @@ +[ + { + "description": "issue678", + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://example.com/issue-470.json", + "title": "OneOf validation message", + "description": "Test description", + "type": "object", + "properties": { + "outerObject": { + "type": "object", + "properties": { + "innerObject": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "unit": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "value", + "unit" + ] + } + ] + } + } + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "OneOfValidator is filtering out the required errors if all the oneOf schemas are having the issues", + "data": { + "outerObject": { + "innerObject": {} + } + }, + "valid": false, + "validationMessages": [ + "$.outerObject.innerObject: object found, string expected", + "$.outerObject.innerObject.value: is missing but it is required", + "$.outerObject.innerObject.unit: is missing but it is required", + "$.outerObject.innerObject: should be valid to one and only one schema, but 0 are valid" + ] + } + ] + } +] \ No newline at end of file diff --git a/src/test/resources/openapi3/AdditionalPropertiesOneOfFailsTest.json b/src/test/resources/openapi3/AdditionalPropertiesOneOfFailsTest.json deleted file mode 100644 index 14424f30f..000000000 --- a/src/test/resources/openapi3/AdditionalPropertiesOneOfFailsTest.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "type": "object", - "properties": { - "locationName": { - "type": "string" - }, - "activities": { - "type": "array", - "items": { - "oneOf": [ - { - "type": "object", - "required": [ - "activityType", - "weight", - "height" - ], - "additionalProperties": false, - "properties": { - "activityType": { - "enum": [ - "machine" - ] - }, - "weight": { - "type": "integer" - }, - "height": { - "type": "integer" - } - } - }, - { - "type": "object", - "required": [ - "activityType", - "chemicalCharacteristic" - ], - "additionalProperties": false, - "properties": { - "activityType": { - "enum": [ - "chemical" - ] - }, - "chemicalCharacteristic": { - "oneOf": [ - { - "type": "object", - "required": [ - "chemicalName" - ], - "additionalProperties": false, - "properties": { - "commonName": { - "type": "string" - }, - "chemicalName": { - "type": "string" - } - } - }, - { - "type": "object", - "required": [ - "chemicalName" - ], - "additionalProperties": false, - "properties": { - "categoryName": { - "type": "string" - }, - "chemicalName": { - "type": "string" - } - } - } - ] - } - } - } - ] - } - } - } -} diff --git a/src/test/resources/schema/issue470-v7.json b/src/test/resources/schema/issue470-v7.json deleted file mode 100644 index 63fa12424..000000000 --- a/src/test/resources/schema/issue470-v7.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://example.com/issue-470.json", - "title": "OneOf validation message", - "description": "Test description", - "type": "object", - "properties": { - "search": { - "type": "object", - "oneOf": [ - { - "type": "object", - "properties": { - "byName": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 20, - "minLength": 1 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "byAge": { - "type": "object", - "properties": { - "age": { - "type": "integer", - "maximum": 150, - "minimum": 1 - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - } - }, - "additionalProperties": false -} diff --git a/src/test/resources/schema/issue491-v7.json b/src/test/resources/schema/issue491-v7.json deleted file mode 100644 index 75c6292b2..000000000 --- a/src/test/resources/schema/issue491-v7.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://example.com/issue-470.json", - "title": "OneOf validation message", - "description": "Test description", - "type": "object", - "properties": { - "search": { - "type": "object", - "oneOf": [ - { - "type": "object", - "properties": { - "searchAge": { - "type": "object", - "properties": { - "age": { - "type": "integer", - "maximum": 150, - "minimum": 1 - } - }, - "required": [ - "age" - ] - } - }, - "required": [ - "searchAge" - ] - }, - { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 20, - "minLength": 1 - } - }, - "required": [ - "name" - ] - } - ] - } - }, - "additionalProperties": false -} diff --git a/src/test/resources/schema/issue491_2-v7.json b/src/test/resources/schema/issue491_2-v7.json deleted file mode 100644 index a4e495180..000000000 --- a/src/test/resources/schema/issue491_2-v7.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://example.com/issue-470.json", - "title": "OneOf validation message", - "description": "Test description", - "type": "object", - "properties": { - "search": { - "type": "object", - "oneOf": [ - { - "type": "object", - "properties": { - "byAge": { - "type": "object", - "properties": { - "age": { - "type": "integer", - "maximum": 150, - "minimum": 1 - } - }, - "required": [ - "age" - ] - } - }, - "required": [ - "byAge" - ] - }, - { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 20, - "minLength": 1 - } - }, - "required": [ - "name" - ] - } - ] - } - }, - "additionalProperties": false -} diff --git a/src/test/resources/schema/issue491_3-v7.json b/src/test/resources/schema/issue491_3-v7.json deleted file mode 100644 index 0d462d44b..000000000 --- a/src/test/resources/schema/issue491_3-v7.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://example.com/issue-470.json", - "title": "OneOf validation message", - "description": "Test description", - "type": "object", - "properties": { - "search": { - "type": "object", - "oneOf": [ - { - "type": "object", - "properties": { - "age": { - "type": "integer", - "maximum": 150, - "minimum": 1 - } - }, - "required": [ - "age" - ] - }, - { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 20, - "minLength": 1 - } - }, - "required": [ - "name" - ] - } - ] - } - }, - "additionalProperties": false -}