diff --git a/src/main/java/com/networknt/schema/format/IdnHostnameFormat.java b/src/main/java/com/networknt/schema/format/IdnHostnameFormat.java index 5d1ec7ee0..7ba0e8606 100644 --- a/src/main/java/com/networknt/schema/format/IdnHostnameFormat.java +++ b/src/main/java/com/networknt/schema/format/IdnHostnameFormat.java @@ -10,7 +10,7 @@ public class IdnHostnameFormat implements Format { @Override public boolean matches(ExecutionContext executionContext, String value) { - if (null == value || value.isEmpty()) return true; + if (null == value) return true; return RFC5892.isValid(value); } diff --git a/src/main/java/com/networknt/schema/utils/RFC5892.java b/src/main/java/com/networknt/schema/utils/RFC5892.java index 35b30686e..a21931cae 100644 --- a/src/main/java/com/networknt/schema/utils/RFC5892.java +++ b/src/main/java/com/networknt/schema/utils/RFC5892.java @@ -88,9 +88,22 @@ private static boolean testAllowedCharacter(String s, int i) { && !isContextJ(c) && !isContextO(c); // RFC 5891 4.2.3.3. Contextual Rules } + /** + * Whenever dots are used as label separators, the following characters MUST be + * recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), + * U+FF0E (fullwidth full stop), U+FF61 (halfwidth ideographic full stop) + */ + private static String LABEL_SEPARATOR_REGEX = "[\\.\\u3002\\uff0e\\uff61]"; + public static boolean isValid(String value) { - // RFC 5892 calls each segment in a host name a label. They are separated by '.'. - String[] labels = value.split("\\."); + if ("".equals(value)) { + return false; // empty string should fail + } + if (value.length() == 1 && value.matches(LABEL_SEPARATOR_REGEX)) { + return false; // single label separator should fail + } + // RFC 5892 calls each segment in a host name a label. They are separated by all the recognized label separators. + String[] labels = value.split(LABEL_SEPARATOR_REGEX); for (String label : labels) { if (label.isEmpty()) continue; // A DNS entry may contain a trailing '.'. diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java index cbab660df..28cd671b3 100644 --- a/src/test/java/com/networknt/schema/UriMappingTest.java +++ b/src/test/java/com/networknt/schema/UriMappingTest.java @@ -43,7 +43,7 @@ class UriMappingTest { */ @Test void testBuilderUriMappingUri() throws IOException { - URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json"); + URL mappings = UriMappingTest.class.getResource("/uri_mapping/uri-mapping.json"); JsonMetaSchema draftV4 = JsonMetaSchema.getV4(); Builder builder = JsonSchemaFactory.builder() .defaultMetaSchemaIri(draftV4.getIri()) @@ -81,7 +81,7 @@ void testBuilderExampleMappings() throws IOException { } catch (Exception ex) { fail("Unexpected exception thrown", ex); } - URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json"); + URL mappings = UriMappingTest.class.getResource("/uri_mapping/invalid-schema-uri.json"); JsonMetaSchema draftV4 = JsonMetaSchema.getV4(); Builder builder = JsonSchemaFactory.builder() .defaultMetaSchemaIri(draftV4.getIri()) @@ -100,7 +100,7 @@ void testBuilderExampleMappings() throws IOException { */ @Test void testValidatorConfigUriMappingUri() throws IOException { - URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json"); + URL mappings = UriMappingTest.class.getResource("/uri_mapping/uri-mapping.json"); JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)) .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build(); JsonSchema schema = instance.getSchema(SchemaLocation.of( @@ -118,7 +118,7 @@ void testValidatorConfigUriMappingUri() throws IOException { */ @Test void testValidatorConfigExampleMappings() throws IOException { - URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json"); + URL mappings = UriMappingTest.class.getResource("/uri_mapping/invalid-schema-uri.json"); JsonSchemaFactory instance = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).build(); SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); @@ -145,11 +145,11 @@ void testValidatorConfigExampleMappings() throws IOException { @Test void testMappingsForRef() throws IOException { - URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/schema-with-ref-mapping.json"); + URL mappings = UriMappingTest.class.getResource("/uri_mapping/schema-with-ref-mapping.json"); JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)) .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build(); SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); - JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:draft4/extra/uri_mapping/schema-with-ref.json"), + JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:uri_mapping/schema-with-ref.json"), config); assertEquals(0, schema.validate(mapper.readTree("[]")).size()); } diff --git a/src/test/java/com/networknt/schema/suite/TestCase.java b/src/test/java/com/networknt/schema/suite/TestCase.java index f7cdc14c0..9aa19e433 100644 --- a/src/test/java/com/networknt/schema/suite/TestCase.java +++ b/src/test/java/com/networknt/schema/suite/TestCase.java @@ -1,6 +1,7 @@ package com.networknt.schema.suite; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; @@ -12,6 +13,7 @@ * An individual test case, containing multiple testSpecs of a single schema's * behavior */ +@JsonIgnoreProperties(ignoreUnknown = true) // ignore specification in additionalProperties.json public class TestCase { /** diff --git a/src/test/resources/draft4/extra/uri_mapping/example-schema.json b/src/test/resources/uri_mapping/example-schema.json similarity index 100% rename from src/test/resources/draft4/extra/uri_mapping/example-schema.json rename to src/test/resources/uri_mapping/example-schema.json diff --git a/src/test/resources/draft4/extra/uri_mapping/invalid-schema-uri.json b/src/test/resources/uri_mapping/invalid-schema-uri.json similarity index 63% rename from src/test/resources/draft4/extra/uri_mapping/invalid-schema-uri.json rename to src/test/resources/uri_mapping/invalid-schema-uri.json index 59ab16133..0990fe6f4 100644 --- a/src/test/resources/draft4/extra/uri_mapping/invalid-schema-uri.json +++ b/src/test/resources/uri_mapping/invalid-schema-uri.json @@ -5,11 +5,11 @@ }, { "publicURL": "http://example.com/invalid/schema/url", - "localURL": "resource:/draft4/extra/uri_mapping/example-schema.json" + "localURL": "resource:/uri_mapping/example-schema.json" }, { "publicURL": "https://example.com/invalid/schema/url", - "localURL": "resource:/draft4/extra/uri_mapping/example-schema.json" + "localURL": "resource:/uri_mapping/example-schema.json" } ] diff --git a/src/test/resources/draft4/extra/uri_mapping/schema-with-ref-mapping.json b/src/test/resources/uri_mapping/schema-with-ref-mapping.json similarity index 71% rename from src/test/resources/draft4/extra/uri_mapping/schema-with-ref-mapping.json rename to src/test/resources/uri_mapping/schema-with-ref-mapping.json index 34e8dfefd..a366c5dca 100644 --- a/src/test/resources/draft4/extra/uri_mapping/schema-with-ref-mapping.json +++ b/src/test/resources/uri_mapping/schema-with-ref-mapping.json @@ -5,6 +5,6 @@ }, { "publicURL": "http://example.com/invalid/schema/url", - "localURL": "resource:/draft4/extra/uri_mapping/example-schema.json" + "localURL": "resource:/uri_mapping/example-schema.json" } ] diff --git a/src/test/resources/draft4/extra/uri_mapping/schema-with-ref.json b/src/test/resources/uri_mapping/schema-with-ref.json similarity index 100% rename from src/test/resources/draft4/extra/uri_mapping/schema-with-ref.json rename to src/test/resources/uri_mapping/schema-with-ref.json diff --git a/src/test/resources/draft4/extra/uri_mapping/uri-mapping.json b/src/test/resources/uri_mapping/uri-mapping.json similarity index 79% rename from src/test/resources/draft4/extra/uri_mapping/uri-mapping.json rename to src/test/resources/uri_mapping/uri-mapping.json index d45284256..981263b74 100644 --- a/src/test/resources/draft4/extra/uri_mapping/uri-mapping.json +++ b/src/test/resources/uri_mapping/uri-mapping.json @@ -5,6 +5,6 @@ }, { "publicURL": "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json", - "localURL": "resource:/draft4/extra/uri_mapping/uri-mapping.schema.json" + "localURL": "resource:/uri_mapping/uri-mapping.schema.json" } ] diff --git a/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json b/src/test/resources/uri_mapping/uri-mapping.schema.json similarity index 100% rename from src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json rename to src/test/resources/uri_mapping/uri-mapping.schema.json diff --git a/src/test/suite/annotations/README.md b/src/test/suite/annotations/README.md new file mode 100644 index 000000000..69cd3dd7e --- /dev/null +++ b/src/test/suite/annotations/README.md @@ -0,0 +1,116 @@ +# Annotations Tests Suite + +The Annotations Test Suite tests which annotations should appear (or not appear) +on which values of an instance. These tests are agnostic of any output format. + +## Supported Dialects + +Although the annotation terminology of didn't appear in the spec until 2019-09, +the concept is compatible with every version of JSON Schema. Test Cases in this +Test Suite are designed to be compatible with as many releases of JSON Schema as +possible. They do not include `$schema` or `$id`/`id` keywords so +implementations can run the same Test Suite for each dialect they support. + +Since this Test Suite can be used for a variety of dialects, there are a couple +of options that can be used by Test Runners to filter out Test Cases that don't +apply to the dialect under test. + +## Test Case Components + +### description + +A short description of what behavior the Test Case is covering. + +### compatibility + +The `compatibility` option allows you to set which dialects the Test Case is +compatible with. Test Runners can use this value to filter out Test Cases that +don't apply the to dialect currently under test. The terminology for annotations +didn't appear in the spec until 2019-09, but the concept is compatible with +older releases as well. When setting `compatibility`, test authors should take +into account dialects before 2019-09 for implementations that chose to support +annotations for older dialects. + +Dialects are indicated by the number corresponding to their release. Date-based +releases use just the year. If this option isn't present, it means the Test Case +is compatible with any dialect. + +If this option is present with a number, the number indicates the minimum +release the Test Case is compatible with. This example indicates that the Test +Case is compatible with draft-07 and up. + +**Example**: `"compatibility": "7"` + +You can use a `<=` operator to indicate that the Test Case is compatible with +releases less then or equal to the given release. This example indicates that +the Test Case is compatible with 2019-09 and under. + +**Example**: `"compatibility": "<=2019"` + +You can use comma-separated values to indicate multiple constraints if needed. +This example indicates that the Test Case is compatible with releases between +draft-06 and 2019-09. + +**Example**: `"compatibility": "6,<=2019"` + +For convenience, you can use the `=` operator to indicate a Test Case is only +compatible with a single release. This example indicates that the Test Case is +compatible only with 2020-12. + +**Example**: `"compatibility": "=2020"` + +### schema + +The schema that will serve as the subject for the tests. Whenever possible, this +schema shouldn't include `$schema` or `id`/`$id` because Test Cases should be +designed to work with as many releases as possible. + +### externalSchemas + +This allows you to define additional schemas that `schema` makes references to. +The value is an object where the keys are retrieval URIs and values are schemas. +Most external schemas aren't self identifying (using `id`/`$id`) and rely on the +retrieval URI for identification. This is done to increase the number of +dialects that the test is compatible with. Because `id` changed to `$id` in +draft-06, if you use `$id`, the test becomes incompatible with draft-03/4 and in +most cases, that's not necessary. + +### tests + +A collection of Tests to run to verify the Test Case. + +## Test Components + +### instance + +The JSON instance to be annotated. + +### assertions + +A collection of assertions that must be true for the test to pass. + +## Assertions Components + +### location + +The instance location. + +### keyword + +The annotating keyword. + +### expected + +A collection of `keyword` annotations expected on the instance at `location`. +`expected` is an object where the keys are schema locations and the values are +the annotation that schema location contributed for the given `keyword`. + +There can be more than one expected annotation because multiple schema locations +could contribute annotations for a single keyword. + +An empty object is an assertion that the annotation must not appear at the +`location` for the `keyword`. + +As a convention for this Test Suite, the `expected` array should be sorted such +that the most recently encountered value for an annotation given top-down +evaluation of the schema comes before previously encountered values. diff --git a/src/test/suite/annotations/assertion.schema.json b/src/test/suite/annotations/assertion.schema.json new file mode 100644 index 000000000..882517887 --- /dev/null +++ b/src/test/suite/annotations/assertion.schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "type": "object", + "properties": { + "location": { + "markdownDescription": "The instance location.", + "type": "string", + "format": "json-pointer" + }, + "keyword": { + "markdownDescription": "The annotation keyword.", + "type": "string" + }, + "expected": { + "markdownDescription": "An object of schemaLocation/annotations pairs for `keyword` annotations expected on the instance at `location`.", + "type": "object", + "propertyNames": { + "format": "uri" + } + } + }, + "required": ["location", "keyword", "expected"] +} diff --git a/src/test/suite/annotations/test-case.schema.json b/src/test/suite/annotations/test-case.schema.json new file mode 100644 index 000000000..6df5f1098 --- /dev/null +++ b/src/test/suite/annotations/test-case.schema.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "type": "object", + "properties": { + "description": { + "markdownDescription": "A short description of what behavior the Test Case is covering.", + "type": "string" + }, + "compatibility": { + "markdownDescription": "Set which dialects the Test Case is compatible with. Examples:\n- `\"7\"` -- draft-07 and above\n- `\"<=2019\"` -- 2019-09 and previous\n- `\"6,<=2019\"` -- Between draft-06 and 2019-09\n- `\"=2020\"` -- 2020-12 only", + "type": "string", + "pattern": "^(<=|=)?([123467]|2019|2020)(,(<=|=)?([123467]|2019|2020))*$" + }, + "schema": { + "markdownDescription": "This schema shouldn't include `$schema` or `id`/`$id` unless necesary for the test because Test Cases should be designed to work with as many releases as possible.", + "type": ["boolean", "object"] + }, + "externalSchemas": { + "markdownDescription": "The keys are retrieval URIs and values are schemas.", + "type": "object", + "patternProperties": { + "": { + "type": ["boolean", "object"] + } + }, + "propertyNames": { + "format": "uri" + } + }, + "tests": { + "markdownDescription": "A collection of Tests to run to verify the Test Case.", + "type": "array", + "items": { "$ref": "./test.schema.json" } + } + }, + "required": ["description", "schema", "tests"] +} diff --git a/src/test/suite/annotations/test-suite.schema.json b/src/test/suite/annotations/test-suite.schema.json new file mode 100644 index 000000000..c8b17f0d5 --- /dev/null +++ b/src/test/suite/annotations/test-suite.schema.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "suite": { + "type": "array", + "items": { "$ref": "./test-case.schema.json" } + } + }, + "required": ["description", "suite"] +} diff --git a/src/test/suite/annotations/test.schema.json b/src/test/suite/annotations/test.schema.json new file mode 100644 index 000000000..3581fbfca --- /dev/null +++ b/src/test/suite/annotations/test.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + + "type": "object", + "properties": { + "instance": { + "markdownDescription": "The JSON instance to be annotated." + }, + "assertions": { + "markdownDescription": "A collection of assertions that must be true for the test to pass.", + "type": "array", + "items": { "$ref": "./assertion.schema.json" } + } + }, + "required": ["instance", "assertions"] +} diff --git a/src/test/suite/annotations/tests/applicators.json b/src/test/suite/annotations/tests/applicators.json new file mode 100644 index 000000000..ceb5044f3 --- /dev/null +++ b/src/test/suite/annotations/tests/applicators.json @@ -0,0 +1,409 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The applicator vocabulary", + "suite": [ + { + "description": "`properties`, `patternProperties`, and `additionalProperties`", + "compatibility": "3", + "schema": { + "properties": { + "foo": { + "title": "Foo" + } + }, + "patternProperties": { + "^a": { + "title": "Bar" + } + }, + "additionalProperties": { + "title": "Baz" + } + }, + "tests": [ + { + "instance": {}, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": {} + }, + { + "location": "/apple", + "keyword": "title", + "expected": {} + }, + { + "location": "/bar", + "keyword": "title", + "expected": {} + } + ] + }, + { + "instance": { + "foo": {}, + "apple": {}, + "baz": {} + }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/properties/foo": "Foo" + } + }, + { + "location": "/apple", + "keyword": "title", + "expected": { + "#/patternProperties/%5Ea": "Bar" + } + }, + { + "location": "/baz", + "keyword": "title", + "expected": { + "#/additionalProperties": "Baz" + } + } + ] + } + ] + }, + { + "description": "`propertyNames` doesn't annotate property values", + "compatibility": "6", + "schema": { + "propertyNames": { + "const": "foo", + "title": "Foo" + } + }, + "tests": [ + { + "instance": { + "foo": 42 + }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": {} + } + ] + } + ] + }, + { + "description": "`prefixItems` and `items`", + "compatibility": "2020", + "schema": { + "prefixItems": [ + { + "title": "Foo" + } + ], + "items": { + "title": "Bar" + } + }, + "tests": [ + { + "instance": [ + "foo", + "bar" + ], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/prefixItems/0": "Foo" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/items": "Bar" + } + }, + { + "location": "/2", + "keyword": "title", + "expected": {} + } + ] + } + ] + }, + { + "description": "`contains`", + "compatibility": "6", + "schema": { + "contains": { + "type": "number", + "title": "Foo" + } + }, + "tests": [ + { + "instance": [ + "foo", + 42, + true + ], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": {} + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/contains": "Foo" + } + }, + { + "location": "/2", + "keyword": "title", + "expected": {} + }, + { + "location": "/3", + "keyword": "title", + "expected": {} + } + ] + } + ] + }, + { + "description": "`allOf`", + "compatibility": "4", + "schema": { + "allOf": [ + { + "title": "Foo" + }, + { + "title": "Bar" + } + ] + }, + "tests": [ + { + "instance": "foo", + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/allOf/1": "Bar", + "#/allOf/0": "Foo" + } + } + ] + } + ] + }, + { + "description": "`anyOf`", + "compatibility": "4", + "schema": { + "anyOf": [ + { + "type": "integer", + "title": "Foo" + }, + { + "type": "number", + "title": "Bar" + } + ] + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/anyOf/1": "Bar", + "#/anyOf/0": "Foo" + } + } + ] + }, + { + "instance": 4.2, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/anyOf/1": "Bar" + } + } + ] + } + ] + }, + { + "description": "`oneOf`", + "compatibility": "4", + "schema": { + "oneOf": [ + { + "type": "string", + "title": "Foo" + }, + { + "type": "number", + "title": "Bar" + } + ] + }, + "tests": [ + { + "instance": "foo", + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/oneOf/0": "Foo" + } + } + ] + }, + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/oneOf/1": "Bar" + } + } + ] + } + ] + }, + { + "description": "`not`", + "compatibility": "4", + "schema": { + "title": "Foo", + "not": { + "not": { + "title": "Bar" + } + } + }, + "tests": [ + { + "instance": {}, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#": "Foo" + } + } + ] + } + ] + }, + { + "description": "`dependentSchemas`", + "compatibility": "2019", + "schema": { + "dependentSchemas": { + "foo": { + "title": "Foo" + } + } + }, + "tests": [ + { + "instance": { + "foo": 42 + }, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/dependentSchemas/foo": "Foo" + } + } + ] + }, + { + "instance": { + "foo": 42 + }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": {} + } + ] + } + ] + }, + { + "description": "`if`, `then`, and `else`", + "compatibility": "7", + "schema": { + "if": { + "title": "If", + "type": "string" + }, + "then": { + "title": "Then" + }, + "else": { + "title": "Else" + } + }, + "tests": [ + { + "instance": "foo", + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/then": "Then", + "#/if": "If" + } + } + ] + }, + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/else": "Else" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/content.json b/src/test/suite/annotations/tests/content.json new file mode 100644 index 000000000..ad5006038 --- /dev/null +++ b/src/test/suite/annotations/tests/content.json @@ -0,0 +1,119 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The content vocabulary", + "suite": [ + { + "description": "`contentMediaType` is an annotation for string instances", + "compatibility": "7", + "schema": { + "contentMediaType": "application/json" + }, + "tests": [ + { + "instance": "{ \"foo\": \"bar\" }", + "assertions": [ + { + "location": "", + "keyword": "contentMediaType", + "expected": { + "#": "application/json" + } + } + ] + }, + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "contentMediaType", + "expected": {} + } + ] + } + ] + }, + { + "description": "`contentEncoding` is an annotation for string instances", + "compatibility": "7", + "schema": { + "contentEncoding": "base64" + }, + "tests": [ + { + "instance": "SGVsbG8gZnJvbSBKU09OIFNjaGVtYQ==", + "assertions": [ + { + "location": "", + "keyword": "contentEncoding", + "expected": { + "#": "base64" + } + } + ] + }, + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "contentEncoding", + "expected": {} + } + ] + } + ] + }, + { + "description": "`contentSchema` is an annotation for string instances", + "compatibility": "2019", + "schema": { + "contentMediaType": "application/json", + "contentSchema": { "type": "number" } + }, + "tests": [ + { + "instance": "42", + "assertions": [ + { + "location": "", + "keyword": "contentSchema", + "expected": { + "#": { "type": "number" } + } + } + ] + }, + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "contentSchema", + "expected": {} + } + ] + } + ] + }, + { + "description": "`contentSchema` requires `contentMediaType`", + "compatibility": "2019", + "schema": { + "contentSchema": { "type": "number" } + }, + "tests": [ + { + "instance": "42", + "assertions": [ + { + "location": "", + "keyword": "contentSchema", + "expected": {} + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/core.json b/src/test/suite/annotations/tests/core.json new file mode 100644 index 000000000..1d8dee556 --- /dev/null +++ b/src/test/suite/annotations/tests/core.json @@ -0,0 +1,30 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The core vocabulary", + "suite": [ + { + "description": "`$ref` and `$defs`", + "compatibility": "2019", + "schema": { + "$ref": "#/$defs/foo", + "$defs": { + "foo": { "title": "Foo" } + } + }, + "tests": [ + { + "instance": "foo", + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#/$defs/foo": "Foo" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/format.json b/src/test/suite/annotations/tests/format.json new file mode 100644 index 000000000..d8cf9a7af --- /dev/null +++ b/src/test/suite/annotations/tests/format.json @@ -0,0 +1,26 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The format vocabulary", + "suite": [ + { + "description": "`format` is an annotation", + "schema": { + "format": "email" + }, + "tests": [ + { + "instance": "foo@bar.com", + "assertions": [ + { + "location": "", + "keyword": "format", + "expected": { + "#": "email" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/meta-data.json b/src/test/suite/annotations/tests/meta-data.json new file mode 100644 index 000000000..be99b652f --- /dev/null +++ b/src/test/suite/annotations/tests/meta-data.json @@ -0,0 +1,150 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The meta-data vocabulary", + "suite": [ + { + "description": "`title` is an annotation", + "schema": { + "title": "Foo" + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "title", + "expected": { + "#": "Foo" + } + } + ] + } + ] + }, + { + "description": "`description` is an annotation", + "schema": { + "description": "Foo" + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "description", + "expected": { + "#": "Foo" + } + } + ] + } + ] + }, + { + "description": "`default` is an annotation", + "schema": { + "default": "Foo" + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "default", + "expected": { + "#": "Foo" + } + } + ] + } + ] + }, + { + "description": "`deprecated` is an annotation", + "compatibility": "2019", + "schema": { + "deprecated": true + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "deprecated", + "expected": { + "#": true + } + } + ] + } + ] + }, + { + "description": "`readOnly` is an annotation", + "compatibility": "7", + "schema": { + "readOnly": true + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "readOnly", + "expected": { + "#": true + } + } + ] + } + ] + }, + { + "description": "`writeOnly` is an annotation", + "compatibility": "7", + "schema": { + "writeOnly": true + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "writeOnly", + "expected": { + "#": true + } + } + ] + } + ] + }, + { + "description": "`examples` is an annotation", + "compatibility": "6", + "schema": { + "examples": ["Foo", "Bar"] + }, + "tests": [ + { + "instance": "Foo", + "assertions": [ + { + "location": "", + "keyword": "examples", + "expected": { + "#": ["Foo", "Bar"] + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/unevaluated.json b/src/test/suite/annotations/tests/unevaluated.json new file mode 100644 index 000000000..9f2db1158 --- /dev/null +++ b/src/test/suite/annotations/tests/unevaluated.json @@ -0,0 +1,661 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "The unevaluated vocabulary", + "suite": [ + { + "description": "`unevaluatedProperties` alone", + "compatibility": "2019", + "schema": { + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `properties`", + "compatibility": "2019", + "schema": { + "properties": { + "foo": { "title": "Evaluated" } + }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/properties/foo": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `patternProperties`", + "compatibility": "2019", + "schema": { + "patternProperties": { + "^a": { "title": "Evaluated" } + }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "apple": 42, "bar": 24 }, + "assertions": [ + { + "location": "/apple", + "keyword": "title", + "expected": { + "#/patternProperties/%5Ea": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `additionalProperties`", + "compatibility": "2019", + "schema": { + "additionalProperties": { "title": "Evaluated" }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/additionalProperties": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/additionalProperties": "Evaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `dependentSchemas`", + "compatibility": "2019", + "schema": { + "dependentSchemas": { + "foo": { + "properties": { + "bar": { "title": "Evaluated" } + } + } + }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/dependentSchemas/foo/properties/bar": "Evaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `if`, `then`, and `else`", + "compatibility": "2019", + "schema": { + "if": { + "properties": { + "foo": { + "type": "string", + "title": "If" + } + } + }, + "then": { + "properties": { + "foo": { "title": "Then" } + } + }, + "else": { + "properties": { + "foo": { "title": "Else" } + } + }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": "", "bar": 42 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/then/properties/foo": "Then", + "#/if/properties/foo": "If" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + }, + { + "instance": { "foo": 42, "bar": "" }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/else/properties/foo": "Else" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `allOf`", + "compatibility": "2019", + "schema": { + "allOf": [ + { + "properties": { + "foo": { "title": "Evaluated" } + } + } + ], + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/allOf/0/properties/foo": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `anyOf`", + "compatibility": "2019", + "schema": { + "anyOf": [ + { + "properties": { + "foo": { "title": "Evaluated" } + } + } + ], + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/anyOf/0/properties/foo": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `oneOf`", + "compatibility": "2019", + "schema": { + "oneOf": [ + { + "properties": { + "foo": { "title": "Evaluated" } + } + } + ], + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/oneOf/0/properties/foo": "Evaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedProperties` with `not`", + "compatibility": "2019", + "schema": { + "not": { + "not": { + "properties": { + "foo": { "title": "Evaluated" } + } + } + }, + "unevaluatedProperties": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": { "foo": 42, "bar": 24 }, + "assertions": [ + { + "location": "/foo", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + }, + { + "location": "/bar", + "keyword": "title", + "expected": { + "#/unevaluatedProperties": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` alone", + "compatibility": "2019", + "schema": { + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `prefixItems`", + "compatibility": "2020", + "schema": { + "prefixItems": [{ "title": "Evaluated" }], + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/prefixItems/0": "Evaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `contains`", + "compatibility": "2020", + "schema": { + "contains": { + "type": "string", + "title": "Evaluated" + }, + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": ["foo", 42], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/contains": "Evaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `if`, `then`, and `else`", + "compatibility": "2020", + "schema": { + "if": { + "prefixItems": [ + { + "type": "string", + "title": "If" + } + ] + }, + "then": { + "prefixItems": [ + { "title": "Then" } + ] + }, + "else": { + "prefixItems": [ + { "title": "Else" } + ] + }, + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": ["", 42], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/then/prefixItems/0": "Then", + "#/if/prefixItems/0": "If" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + }, + { + "instance": [42, ""], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/else/prefixItems/0": "Else" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `allOf`", + "compatibility": "2020", + "schema": { + "allOf": [ + { + "prefixItems": [ + { "title": "Evaluated" } + ] + } + ], + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/allOf/0/prefixItems/0": "Evaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `anyOf`", + "compatibility": "2020", + "schema": { + "anyOf": [ + { + "prefixItems": [ + { "title": "Evaluated" } + ] + } + ], + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/anyOf/0/prefixItems/0": "Evaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `oneOf`", + "compatibility": "2020", + "schema": { + "oneOf": [ + { + "prefixItems": [ + { "title": "Evaluated" } + ] + } + ], + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/oneOf/0/prefixItems/0": "Evaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + }, + { + "description": "`unevaluatedItems` with `not`", + "compatibility": "2020", + "schema": { + "not": { + "not": { + "prefixItems": [ + { "title": "Evaluated" } + ] + } + }, + "unevaluatedItems": { "title": "Unevaluated" } + }, + "tests": [ + { + "instance": [42, 24], + "assertions": [ + { + "location": "/0", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + }, + { + "location": "/1", + "keyword": "title", + "expected": { + "#/unevaluatedItems": "Unevaluated" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/annotations/tests/unknown.json b/src/test/suite/annotations/tests/unknown.json new file mode 100644 index 000000000..b0c89003c --- /dev/null +++ b/src/test/suite/annotations/tests/unknown.json @@ -0,0 +1,27 @@ +{ + "$schema": "../test-suite.schema.json", + "description": "Unknown keywords", + "suite": [ + { + "description": "`unknownKeyword` is an annotation", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "x-unknownKeyword": "Foo" + }, + "tests": [ + { + "instance": 42, + "assertions": [ + { + "location": "", + "keyword": "x-unknownKeyword", + "expected": { + "#": "Foo" + } + } + ] + } + ] + } + ] +} diff --git a/src/test/suite/bin/annotate-specification-links b/src/test/suite/bin/annotate-specification-links new file mode 100644 index 000000000..963768b43 --- /dev/null +++ b/src/test/suite/bin/annotate-specification-links @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +""" +Annotate pull requests to the GitHub repository with links to specifications. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any +import json +import re +import sys + +from uritemplate import URITemplate + + +BIN_DIR = Path(__file__).parent +TESTS = BIN_DIR.parent / "tests" +URLS = json.loads(BIN_DIR.joinpath("specification_urls.json").read_text()) + + +def urls(version: str) -> dict[str, URITemplate]: + """ + Retrieve the version-specific URLs for specifications. + """ + for_version = {**URLS["json-schema"][version], **URLS["external"]} + return {k: URITemplate(v) for k, v in for_version.items()} + + +def annotation( + path: Path, + message: str, + line: int = 1, + level: str = "notice", + **kwargs: Any, +) -> str: + """ + Format a GitHub annotation. + + See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions + for full syntax. + """ + + if kwargs: + additional = "," + ",".join(f"{k}={v}" for k, v in kwargs.items()) + else: + additional = "" + + relative = path.relative_to(TESTS.parent) + return f"::{level} file={relative},line={line}{additional}::{message}\n" + + +def line_number_of(path: Path, case: dict[str, Any]) -> int: + """ + Crudely find the line number of a test case. + """ + with path.open() as file: + description = case["description"] + return next( + (i + 1 for i, line in enumerate(file, 1) if description in line), + 1, + ) + +def extract_kind_and_spec(key: str) -> (str, str): + """ + Extracts specification number and kind from the defined key + """ + can_have_spec = ["rfc", "iso"] + if not any(key.startswith(el) for el in can_have_spec): + return key, "" + number = re.search(r"\d+", key) + spec = "" if number is None else number.group(0) + kind = key.removesuffix(spec) + return kind, spec + + +def main(): + # Clear annotations which may have been emitted by a previous run. + sys.stdout.write("::remove-matcher owner=me::\n") + + for version in TESTS.iterdir(): + if version.name in {"draft-next", "latest"}: + continue + + version_urls = urls(version.name) + + for path in version.rglob("*.json"): + try: + contents = json.loads(path.read_text()) + except json.JSONDecodeError as error: + error = annotation( + level="error", + path=path, + line=error.lineno, + col=error.pos + 1, + title=str(error), + message=f"cannot load {path}" + ) + sys.stdout.write(error) + continue + + for test_case in contents: + specifications = test_case.get("specification") + if specifications is not None: + for each in specifications: + quote = each.pop("quote", "") + (key, section), = each.items() + + (kind, spec) = extract_kind_and_spec(key) + + url_template = version_urls[kind] + if url_template is None: + error = annotation( + level="error", + path=path, + line=line_number_of(path, test_case), + title=f"unsupported template '{kind}'", + message=f"cannot find a URL template for '{kind}'" + ) + sys.stdout.write(error) + continue + + url = url_template.expand( + spec=spec, + section=section, + ) + + message = f"{url}\n\n{quote}" if quote else url + sys.stdout.write( + annotation( + path=path, + line=line_number_of(path, test_case), + title="Specification Link", + message=message, + ), + ) + + +if __name__ == "__main__": + main() diff --git a/src/test/suite/bin/annotation-tests.ts b/src/test/suite/bin/annotation-tests.ts new file mode 100644 index 000000000..2d3d19326 --- /dev/null +++ b/src/test/suite/bin/annotation-tests.ts @@ -0,0 +1,31 @@ +#!/usr/bin/env deno +import { validate } from "npm:@hyperjump/json-schema/draft-07"; +import { BASIC } from "npm:@hyperjump/json-schema/experimental"; + +const validateTestSuite = await validate("./annotations/test-suite.schema.json"); + +console.log("Validating annotation tests ..."); + +let isValid = true; +for await (const entry of Deno.readDir("./annotations/tests")) { + if (entry.isFile) { + const json = await Deno.readTextFile(`./annotations/tests/${entry.name}`); + const suite = JSON.parse(json); + + const output = validateTestSuite(suite, BASIC); + + if (output.valid) { + console.log(`\x1b[32m✔\x1b[0m ${entry.name}`); + } else { + isValid = false; + console.log(`\x1b[31m✖\x1b[0m ${entry.name}`); + console.log(output); + } + } +} + +console.log("Done."); + +if (!isValid) { + Deno.exit(1); +} diff --git a/src/test/suite/bin/jsonschema_suite b/src/test/suite/bin/jsonschema_suite index 9fee8d7ba..c83e7cb2c 100755 --- a/src/test/suite/bin/jsonschema_suite +++ b/src/test/suite/bin/jsonschema_suite @@ -14,9 +14,11 @@ import warnings try: import jsonschema.validators except ImportError: - jsonschema = None + jsonschema = Unresolvable = None VALIDATORS = {} else: + from referencing.exceptions import Unresolvable + VALIDATORS = { "draft3": jsonschema.validators.Draft3Validator, "draft4": jsonschema.validators.Draft4Validator, @@ -587,7 +589,7 @@ class SanityTests(unittest.TestCase): with self.subTest(case=case, version=version.name): try: Validator(case["schema"]).is_valid(12) - except jsonschema.exceptions.RefResolutionError: + except Unresolvable: pass @unittest.skipIf(jsonschema is None, "Validation library not present!") @@ -615,9 +617,6 @@ class SanityTests(unittest.TestCase): with self.subTest(path=path): try: validator.validate(cases) - except jsonschema.exceptions.RefResolutionError: - # python-jsonschema/jsonschema#884 - pass except jsonschema.ValidationError as error: self.fail(str(error)) diff --git a/src/test/suite/bin/specification_urls.json b/src/test/suite/bin/specification_urls.json new file mode 100644 index 000000000..e1824999a --- /dev/null +++ b/src/test/suite/bin/specification_urls.json @@ -0,0 +1,34 @@ +{ + "json-schema": { + "draft2020-12": { + "core": "https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#section-{section}", + "validation": "https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-{section}" + }, + "draft2019-09": { + "core": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-02#rfc.section.{section}", + "validation": "https://json-schema.org/draft/2019-09/draft-handrews-json-schema-validation-02#rfc.section.{section}" + }, + "draft7": { + "core": "https://json-schema.org/draft-07/draft-handrews-json-schema-01#rfc.section.{section}", + "validation": "https://json-schema.org/draft-07/draft-handrews-json-schema-validation-01#rfc.section.{section}" + }, + "draft6": { + "core": "https://json-schema.org/draft-06/draft-wright-json-schema-01#rfc.section.{section}", + "validation": "https://json-schema.org/draft-06/draft-wright-json-schema-validation-01#rfc.section.{section}" + }, + "draft4": { + "core": "https://json-schema.org/draft-04/draft-zyp-json-schema-04#rfc.section.{section}", + "validation": "https://json-schema.org/draft-04/draft-fge-json-schema-validation-00#rfc.section.{section}" + }, + "draft3": { + "core": "https://json-schema.org/draft-03/draft-zyp-json-schema-03.pdf" + } + }, + + "external": { + "ecma262": "https://262.ecma-international.org/{section}", + "perl5": "https://perldoc.perl.org/perlre#{section}", + "rfc": "https://www.rfc-editor.org/rfc/rfc{spec}.html#section-{section}", + "iso": "https://www.iso.org/obp/ui" + } +} diff --git a/src/test/suite/output-tests/draft-next/content/general.json b/src/test/suite/output-tests/draft-next/content/general.json index 27082ed66..23fe1daeb 100644 --- a/src/test/suite/output-tests/draft-next/content/general.json +++ b/src/test/suite/output-tests/draft-next/content/general.json @@ -20,7 +20,7 @@ "contains": { "properties": { "evaluationPath": {"const": ""}, - "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/general/0"}, + "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/general/0#"}, "instanceLocation": {"const": ""}, "annotations": false, "droppedAnnotations": { diff --git a/src/test/suite/output-tests/draft-next/content/readOnly.json b/src/test/suite/output-tests/draft-next/content/readOnly.json index d387d9324..8d1c2dec2 100644 --- a/src/test/suite/output-tests/draft-next/content/readOnly.json +++ b/src/test/suite/output-tests/draft-next/content/readOnly.json @@ -19,7 +19,7 @@ "contains": { "properties": { "evaluationPath": {"const": ""}, - "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/readOnly/0"}, + "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/readOnly/0#"}, "instanceLocation": {"const": ""}, "annotations": { "properties": { diff --git a/src/test/suite/output-tests/draft-next/content/type.json b/src/test/suite/output-tests/draft-next/content/type.json index e17f1f530..afc7f5fd1 100644 --- a/src/test/suite/output-tests/draft-next/content/type.json +++ b/src/test/suite/output-tests/draft-next/content/type.json @@ -19,7 +19,7 @@ "contains": { "properties": { "evaluationPath": {"const": ""}, - "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/type/0"}, + "schemaLocation": {"const": "https://json-schema.org/tests/content/draft-next/type/0#"}, "instanceLocation": {"const": ""}, "annotations": false, "errors": { diff --git a/src/test/suite/output-tests/draft2019-09/content/type.json b/src/test/suite/output-tests/draft2019-09/content/type.json index cff77a740..21118fd5f 100644 --- a/src/test/suite/output-tests/draft2019-09/content/type.json +++ b/src/test/suite/output-tests/draft2019-09/content/type.json @@ -31,32 +31,6 @@ "required": ["errors"] } } - }, - { - "description": "correct type yields an output unit", - "data": "a string", - "output": { - "basic": { - "$id": "https://json-schema.org/tests/content/draft2019-09/type/0/tests/1/basic", - "$ref": "/draft/2019-09/output/schema", - "properties": { - "annotations": { - "contains": { - "properties": { - "valid": {"const": true}, - "keywordLocation": {"const": "/type"}, - "absoluteKeywordLocation": {"const": "https://json-schema.org/tests/content/draft2019-09/type/0#/type"}, - "instanceLocation": {"const": ""}, - "annotation": false, - "error": false - }, - "required": ["keywordLocation", "instanceLocation"] - } - } - }, - "required": ["annotations"] - } - } } ] } diff --git a/src/test/suite/output-tests/draft2020-12/content/type.json b/src/test/suite/output-tests/draft2020-12/content/type.json index 710475b2b..2949a6052 100644 --- a/src/test/suite/output-tests/draft2020-12/content/type.json +++ b/src/test/suite/output-tests/draft2020-12/content/type.json @@ -31,32 +31,6 @@ "required": ["errors"] } } - }, - { - "description": "correct type yields an output unit", - "data": "a string", - "output": { - "basic": { - "$id": "https://json-schema.org/tests/content/draft2020-12/type/0/tests/1/basic", - "$ref": "/draft/2020-12/output/schema", - "properties": { - "annotations": { - "contains": { - "properties": { - "valid": {"const": true}, - "keywordLocation": {"const": "/type"}, - "absoluteKeywordLocation": {"const": "https://json-schema.org/tests/content/draft2020-12/type/0#/type"}, - "instanceLocation": {"const": ""}, - "annotation": false, - "error": false - }, - "required": ["keywordLocation", "instanceLocation"] - } - } - }, - "required": ["annotations"] - } - } } ] } diff --git a/src/test/suite/remotes/draft-next/format-assertion-false.json b/src/test/suite/remotes/draft-next/format-assertion-false.json index 91c866996..9cbd2a1de 100644 --- a/src/test/suite/remotes/draft-next/format-assertion-false.json +++ b/src/test/suite/remotes/draft-next/format-assertion-false.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/next/vocab/core": true, "https://json-schema.org/draft/next/vocab/format-assertion": false }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/next/meta/core" }, { "$ref": "https://json-schema.org/draft/next/meta/format-assertion" } diff --git a/src/test/suite/remotes/draft-next/format-assertion-true.json b/src/test/suite/remotes/draft-next/format-assertion-true.json index a33d1435f..b3ff69f3a 100644 --- a/src/test/suite/remotes/draft-next/format-assertion-true.json +++ b/src/test/suite/remotes/draft-next/format-assertion-true.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/next/vocab/core": true, "https://json-schema.org/draft/next/vocab/format-assertion": true }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/next/meta/core" }, { "$ref": "https://json-schema.org/draft/next/meta/format-assertion" } diff --git a/src/test/suite/remotes/draft-next/id/my_identifier.json b/src/test/suite/remotes/draft-next/id/my_identifier.json deleted file mode 100644 index 1167cb1d9..000000000 --- a/src/test/suite/remotes/draft-next/id/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/next/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/draft-next/metaschema-no-validation.json b/src/test/suite/remotes/draft-next/metaschema-no-validation.json index c19c9e8a7..90e32a672 100644 --- a/src/test/suite/remotes/draft-next/metaschema-no-validation.json +++ b/src/test/suite/remotes/draft-next/metaschema-no-validation.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/next/vocab/applicator": true, "https://json-schema.org/draft/next/vocab/core": true }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/next/meta/applicator" }, { "$ref": "https://json-schema.org/draft/next/meta/core" } diff --git a/src/test/suite/remotes/draft-next/metaschema-optional-vocabulary.json b/src/test/suite/remotes/draft-next/metaschema-optional-vocabulary.json index e78e531d4..1af0cad4c 100644 --- a/src/test/suite/remotes/draft-next/metaschema-optional-vocabulary.json +++ b/src/test/suite/remotes/draft-next/metaschema-optional-vocabulary.json @@ -6,6 +6,7 @@ "https://json-schema.org/draft/next/vocab/core": true, "http://localhost:1234/draft/next/vocab/custom": false }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/next/meta/validation" }, { "$ref": "https://json-schema.org/draft/next/meta/core" } diff --git a/src/test/suite/remotes/draft-next/subSchemas-defs.json b/src/test/suite/remotes/draft-next/subSchemas-defs.json deleted file mode 100644 index 75b7583ca..000000000 --- a/src/test/suite/remotes/draft-next/subSchemas-defs.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/next/schema", - "$defs": { - "integer": { - "type": "integer" - }, - "refToInteger": { - "$ref": "#/$defs/integer" - } - } -} diff --git a/src/test/suite/remotes/draft-next/unknownKeyword/my_identifier.json b/src/test/suite/remotes/draft-next/unknownKeyword/my_identifier.json deleted file mode 100644 index 1167cb1d9..000000000 --- a/src/test/suite/remotes/draft-next/unknownKeyword/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/next/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/draft2019-09/id/my_identifier.json b/src/test/suite/remotes/draft2019-09/id/my_identifier.json deleted file mode 100644 index 2a2c062f3..000000000 --- a/src/test/suite/remotes/draft2019-09/id/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/draft2019-09/metaschema-no-validation.json b/src/test/suite/remotes/draft2019-09/metaschema-no-validation.json index 494f0abff..859006c27 100644 --- a/src/test/suite/remotes/draft2019-09/metaschema-no-validation.json +++ b/src/test/suite/remotes/draft2019-09/metaschema-no-validation.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/2019-09/vocab/applicator": true, "https://json-schema.org/draft/2019-09/vocab/core": true }, + "$recursiveAnchor": true, "allOf": [ { "$ref": "https://json-schema.org/draft/2019-09/meta/applicator" }, { "$ref": "https://json-schema.org/draft/2019-09/meta/core" } diff --git a/src/test/suite/remotes/draft2019-09/metaschema-optional-vocabulary.json b/src/test/suite/remotes/draft2019-09/metaschema-optional-vocabulary.json index 968597c45..3a7502a21 100644 --- a/src/test/suite/remotes/draft2019-09/metaschema-optional-vocabulary.json +++ b/src/test/suite/remotes/draft2019-09/metaschema-optional-vocabulary.json @@ -6,6 +6,7 @@ "https://json-schema.org/draft/2019-09/vocab/core": true, "http://localhost:1234/draft/2019-09/vocab/custom": false }, + "$recursiveAnchor": true, "allOf": [ { "$ref": "https://json-schema.org/draft/2019-09/meta/validation" }, { "$ref": "https://json-schema.org/draft/2019-09/meta/core" } diff --git a/src/test/suite/remotes/draft2019-09/subSchemas-defs.json b/src/test/suite/remotes/draft2019-09/subSchemas-defs.json deleted file mode 100644 index fdfee68d9..000000000 --- a/src/test/suite/remotes/draft2019-09/subSchemas-defs.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$defs": { - "integer": { - "type": "integer" - }, - "refToInteger": { - "$ref": "#/$defs/integer" - } - } -} diff --git a/src/test/suite/remotes/draft2019-09/unknownKeyword/my_identifier.json b/src/test/suite/remotes/draft2019-09/unknownKeyword/my_identifier.json deleted file mode 100644 index 2a2c062f3..000000000 --- a/src/test/suite/remotes/draft2019-09/unknownKeyword/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2019-09/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/draft2020-12/format-assertion-false.json b/src/test/suite/remotes/draft2020-12/format-assertion-false.json index d6dd645b6..43a711c9d 100644 --- a/src/test/suite/remotes/draft2020-12/format-assertion-false.json +++ b/src/test/suite/remotes/draft2020-12/format-assertion-false.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/format-assertion": false }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/meta/core" }, { "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" } diff --git a/src/test/suite/remotes/draft2020-12/format-assertion-true.json b/src/test/suite/remotes/draft2020-12/format-assertion-true.json index bb16d5864..39c6b0abf 100644 --- a/src/test/suite/remotes/draft2020-12/format-assertion-true.json +++ b/src/test/suite/remotes/draft2020-12/format-assertion-true.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/2020-12/vocab/core": true, "https://json-schema.org/draft/2020-12/vocab/format-assertion": true }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/meta/core" }, { "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" } diff --git a/src/test/suite/remotes/draft2020-12/id/my_identifier.json b/src/test/suite/remotes/draft2020-12/id/my_identifier.json deleted file mode 100644 index 3a1a68744..000000000 --- a/src/test/suite/remotes/draft2020-12/id/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/draft2020-12/metaschema-no-validation.json b/src/test/suite/remotes/draft2020-12/metaschema-no-validation.json index 85d74b213..71be8b5da 100644 --- a/src/test/suite/remotes/draft2020-12/metaschema-no-validation.json +++ b/src/test/suite/remotes/draft2020-12/metaschema-no-validation.json @@ -5,6 +5,7 @@ "https://json-schema.org/draft/2020-12/vocab/applicator": true, "https://json-schema.org/draft/2020-12/vocab/core": true }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/meta/applicator" }, { "$ref": "https://json-schema.org/draft/2020-12/meta/core" } diff --git a/src/test/suite/remotes/draft2020-12/metaschema-optional-vocabulary.json b/src/test/suite/remotes/draft2020-12/metaschema-optional-vocabulary.json index f38ec281d..a6963e548 100644 --- a/src/test/suite/remotes/draft2020-12/metaschema-optional-vocabulary.json +++ b/src/test/suite/remotes/draft2020-12/metaschema-optional-vocabulary.json @@ -6,6 +6,7 @@ "https://json-schema.org/draft/2020-12/vocab/core": true, "http://localhost:1234/draft/2020-12/vocab/custom": false }, + "$dynamicAnchor": "meta", "allOf": [ { "$ref": "https://json-schema.org/draft/2020-12/meta/validation" }, { "$ref": "https://json-schema.org/draft/2020-12/meta/core" } diff --git a/src/test/suite/remotes/draft2020-12/subSchemas-defs.json b/src/test/suite/remotes/draft2020-12/subSchemas-defs.json deleted file mode 100644 index 1bb4846d7..000000000 --- a/src/test/suite/remotes/draft2020-12/subSchemas-defs.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "integer": { - "type": "integer" - }, - "refToInteger": { - "$ref": "#/$defs/integer" - } - } -} diff --git a/src/test/suite/remotes/draft2020-12/unknownKeyword/my_identifier.json b/src/test/suite/remotes/draft2020-12/unknownKeyword/my_identifier.json deleted file mode 100644 index 3a1a68744..000000000 --- a/src/test/suite/remotes/draft2020-12/unknownKeyword/my_identifier.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/subSchemas.json b/src/test/suite/remotes/draft3/subSchemas.json similarity index 100% rename from src/test/suite/remotes/subSchemas.json rename to src/test/suite/remotes/draft3/subSchemas.json diff --git a/src/test/suite/remotes/locationIndependentIdentifierDraft4.json b/src/test/suite/remotes/draft4/locationIndependentIdentifier.json similarity index 100% rename from src/test/suite/remotes/locationIndependentIdentifierDraft4.json rename to src/test/suite/remotes/draft4/locationIndependentIdentifier.json diff --git a/src/test/suite/remotes/name.json b/src/test/suite/remotes/draft4/name.json similarity index 100% rename from src/test/suite/remotes/name.json rename to src/test/suite/remotes/draft4/name.json diff --git a/src/test/suite/remotes/subSchemas-defs.json b/src/test/suite/remotes/draft4/subSchemas.json similarity index 62% rename from src/test/suite/remotes/subSchemas-defs.json rename to src/test/suite/remotes/draft4/subSchemas.json index 50b7b6dc4..6e9b3de35 100644 --- a/src/test/suite/remotes/subSchemas-defs.json +++ b/src/test/suite/remotes/draft4/subSchemas.json @@ -1,10 +1,10 @@ { - "$defs": { + "definitions": { "integer": { "type": "integer" }, "refToInteger": { - "$ref": "#/$defs/integer" + "$ref": "#/definitions/integer" } } } diff --git a/src/test/suite/remotes/locationIndependentIdentifierPre2019.json b/src/test/suite/remotes/draft6/locationIndependentIdentifier.json similarity index 100% rename from src/test/suite/remotes/locationIndependentIdentifierPre2019.json rename to src/test/suite/remotes/draft6/locationIndependentIdentifier.json diff --git a/src/test/suite/remotes/draft6/name.json b/src/test/suite/remotes/draft6/name.json new file mode 100644 index 000000000..fceacb809 --- /dev/null +++ b/src/test/suite/remotes/draft6/name.json @@ -0,0 +1,15 @@ +{ + "definitions": { + "orNull": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#" + } + ] + } + }, + "type": "string" +} diff --git a/src/test/suite/remotes/ref-and-definitions.json b/src/test/suite/remotes/draft6/ref-and-definitions.json similarity index 74% rename from src/test/suite/remotes/ref-and-definitions.json rename to src/test/suite/remotes/draft6/ref-and-definitions.json index e0ee802a9..b80deeb7b 100644 --- a/src/test/suite/remotes/ref-and-definitions.json +++ b/src/test/suite/remotes/draft6/ref-and-definitions.json @@ -1,5 +1,5 @@ { - "$id": "http://localhost:1234/ref-and-definitions.json", + "$id": "http://localhost:1234/draft6/ref-and-definitions.json", "definitions": { "inner": { "properties": { diff --git a/src/test/suite/remotes/draft6/subSchemas.json b/src/test/suite/remotes/draft6/subSchemas.json new file mode 100644 index 000000000..6e9b3de35 --- /dev/null +++ b/src/test/suite/remotes/draft6/subSchemas.json @@ -0,0 +1,10 @@ +{ + "definitions": { + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/definitions/integer" + } + } +} diff --git a/src/test/suite/remotes/draft7/locationIndependentIdentifier.json b/src/test/suite/remotes/draft7/locationIndependentIdentifier.json new file mode 100644 index 000000000..e72815cd5 --- /dev/null +++ b/src/test/suite/remotes/draft7/locationIndependentIdentifier.json @@ -0,0 +1,11 @@ +{ + "definitions": { + "refToInteger": { + "$ref": "#foo" + }, + "A": { + "$id": "#foo", + "type": "integer" + } + } +} diff --git a/src/test/suite/remotes/draft7/name.json b/src/test/suite/remotes/draft7/name.json new file mode 100644 index 000000000..fceacb809 --- /dev/null +++ b/src/test/suite/remotes/draft7/name.json @@ -0,0 +1,15 @@ +{ + "definitions": { + "orNull": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#" + } + ] + } + }, + "type": "string" +} diff --git a/src/test/suite/remotes/draft7/ref-and-definitions.json b/src/test/suite/remotes/draft7/ref-and-definitions.json new file mode 100644 index 000000000..d5929380c --- /dev/null +++ b/src/test/suite/remotes/draft7/ref-and-definitions.json @@ -0,0 +1,11 @@ +{ + "$id": "http://localhost:1234/draft7/ref-and-definitions.json", + "definitions": { + "inner": { + "properties": { + "bar": { "type": "string" } + } + } + }, + "allOf": [ { "$ref": "#/definitions/inner" } ] +} diff --git a/src/test/suite/remotes/draft7/subSchemas.json b/src/test/suite/remotes/draft7/subSchemas.json new file mode 100644 index 000000000..6e9b3de35 --- /dev/null +++ b/src/test/suite/remotes/draft7/subSchemas.json @@ -0,0 +1,10 @@ +{ + "definitions": { + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/definitions/integer" + } + } +} diff --git a/src/test/suite/remotes/id/my_identifier.json b/src/test/suite/remotes/id/my_identifier.json deleted file mode 100644 index 5d2d1e511..000000000 --- a/src/test/suite/remotes/id/my_identifier.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/remotes/unknownKeyword/my_identifier.json b/src/test/suite/remotes/unknownKeyword/my_identifier.json deleted file mode 100644 index 5d2d1e511..000000000 --- a/src/test/suite/remotes/unknownKeyword/my_identifier.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "string" -} \ No newline at end of file diff --git a/src/test/suite/test-schema.json b/src/test/suite/test-schema.json index 833931620..0087c5e3d 100644 --- a/src/test/suite/test-schema.json +++ b/src/test/suite/test-schema.json @@ -27,6 +27,69 @@ "type": "array", "items": { "$ref": "#/$defs/test" }, "minItems": 1 + }, + "specification":{ + "description": "A reference to a specification document which defines the behavior tested by this test case. Typically this should be a JSON Schema specification document, though in cases where the JSON Schema specification points to another RFC it should contain *both* the portion of the JSON Schema specification which indicates what RFC (and section) to follow as *well* as information on where in that specification the behavior is specified.", + + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items":{ + "properties": { + "core": { + "description": "A section in official JSON Schema core drafts", + "url": "https://json-schema.org/specification-links", + "pattern": "^[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)*$", + "type":"string" + }, + "validation": { + "description": "A section in official JSON Schema validation drafts", + "url": "https://json-schema.org/specification-links", + "pattern": "^[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)*$", + "type":"string" + }, + "ecma262": { + "description": "A section in official ECMA 262 specification for defining regular expressions", + "url": "https://262.ecma-international.org/", + "pattern": "^[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)*$", + "type":"string" + }, + "perl5": { + "description": "A section name in Perl documentation for defining regular expressions", + "url": "https://perldoc.perl.org/perlre", + "type":"string" + }, + "quote": { + "description": "Quote describing the test case", + "type":"string" + } + }, + "patternProperties": { + "^rfc\\d+$": { + "description": "A section in official RFC for the given rfc number", + "url": "https://www.rfc-editor.org/", + "pattern": "^[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)*$", + "type":"string" + }, + "^iso\\d+$": { + "description": "A section in official ISO for the given iso number", + "pattern": "^[0-9a-zA-Z]+(\\.[0-9a-zA-Z]+)*$", + "type": "string" + } + }, + "additionalProperties": { "type": "string" }, + "minProperties": 1, + "propertyNames": { + "oneOf": [ + { + "pattern": "^((iso)|(rfc))[0-9]+$" + }, + { + "enum": [ "core", "validation", "ecma262", "perl5", "quote" ] + } + ] + } + } } }, "additionalProperties": false diff --git a/src/test/suite/tests/draft-next/additionalProperties.json b/src/test/suite/tests/draft-next/additionalProperties.json index 7859fbbf1..51b0edada 100644 --- a/src/test/suite/tests/draft-next/additionalProperties.json +++ b/src/test/suite/tests/draft-next/additionalProperties.json @@ -152,5 +152,97 @@ "valid": true } ] + }, + { + "description": "additionalProperties with propertyNames", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "propertyNames": { + "maxLength": 5 + }, + "additionalProperties": { + "type": "number" + } + }, + "tests": [ + { + "description": "Valid against both keywords", + "data": { "apple": 4 }, + "valid": true + }, + { + "description": "Valid against propertyNames, but not additionalProperties", + "data": { "fig": 2, "pear": "available" }, + "valid": false + } + ] + }, + { + "description": "propertyDependencies with additionalProperties", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "properties" : {"foo2" : {}}, + "propertyDependencies": { + "foo" : {}, + "foo2": { + "bar": { + "properties": { + "buz": {} + } + } + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "additionalProperties doesn't consider propertyDependencies properties" , + "data": {"foo": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see buz even when foo2 is present", + "data": {"foo2": "bar", "buz": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see buz", + "data": {"buz": ""}, + "valid": false + } + ] + }, + { + "description": "dependentSchemas with additionalProperties", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo": {}, + "foo2": { + "properties": { + "bar": {} + } + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "additionalProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar even when foo2 is present", + "data": {"foo2": "", "bar": ""}, + "valid": false + } + ] } ] diff --git a/src/test/suite/tests/draft-next/anchor.json b/src/test/suite/tests/draft-next/anchor.json index a0c4c51a5..84d4851ca 100644 --- a/src/test/suite/tests/draft-next/anchor.json +++ b/src/test/suite/tests/draft-next/anchor.json @@ -116,29 +116,5 @@ "valid": false } ] - }, - { - "description": "invalid anchors", - "schema": { - "$schema": "https://json-schema.org/draft/next/schema", - "$ref": "https://json-schema.org/draft/next/schema" - }, - "tests": [ - { - "description": "MUST start with a letter (and not #)", - "data": { "$anchor" : "#foo" }, - "valid": false - }, - { - "description": "JSON pointers are not valid", - "data": { "$anchor" : "/a/b" }, - "valid": false - }, - { - "description": "invalid with valid beginning", - "data": { "$anchor" : "foo#something" }, - "valid": false - } - ] } ] diff --git a/src/test/suite/tests/draft-next/dynamicRef.json b/src/test/suite/tests/draft-next/dynamicRef.json index 94124fff6..30821c5b1 100644 --- a/src/test/suite/tests/draft-next/dynamicRef.json +++ b/src/test/suite/tests/draft-next/dynamicRef.json @@ -642,5 +642,60 @@ "valid": false } ] + }, + { + "description": "$dynamicRef skips over intermediate resources - direct reference", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "$id": "https://test.json-schema.org/dynamic-ref-skips-intermediate-resource/main", + "type": "object", + "properties": { + "bar-item": { + "$ref": "item" + } + }, + "$defs": { + "bar": { + "$id": "bar", + "type": "array", + "items": { + "$ref": "item" + }, + "$defs": { + "item": { + "$id": "item", + "type": "object", + "properties": { + "content": { + "$dynamicRef": "#content" + } + }, + "$defs": { + "defaultContent": { + "$dynamicAnchor": "content", + "type": "integer" + } + } + }, + "content": { + "$dynamicAnchor": "content", + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "integer property passes", + "data": { "bar-item": { "content": 42 } }, + "valid": true + }, + { + "description": "string property fails", + "data": { "bar-item": { "content": "value" } }, + "valid": false + } + ] } ] diff --git a/src/test/suite/tests/draft-next/id.json b/src/test/suite/tests/draft-next/id.json deleted file mode 100644 index fe74c6bff..000000000 --- a/src/test/suite/tests/draft-next/id.json +++ /dev/null @@ -1,211 +0,0 @@ -[ - { - "description": "Invalid use of fragments in location-independent $id", - "schema": { - "$schema": "https://json-schema.org/draft/next/schema", - "$ref": "https://json-schema.org/draft/next/schema" - }, - "tests": [ - { - "description": "Identifier name", - "data": { - "$ref": "#foo", - "$defs": { - "A": { - "$id": "#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name and no ref", - "data": { - "$defs": { - "A": { "$id": "#foo" } - } - }, - "valid": false - }, - { - "description": "Identifier path", - "data": { - "$ref": "#/a/b", - "$defs": { - "A": { - "$id": "#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft-next/bar#foo", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/bar#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier path with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft-next/bar#/a/b", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/bar#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft-next/root", - "$ref": "http://localhost:1234/draft-next/nested.json#foo", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#foo", - "type": "integer" - } - } - } - } - }, - "valid": false - }, - { - "description": "Identifier path with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft-next/root", - "$ref": "http://localhost:1234/draft-next/nested.json#/a/b", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#/a/b", - "type": "integer" - } - } - } - } - }, - "valid": false - } - ] - }, - { - "description": "Valid use of empty fragments in location-independent $id", - "comment": "These are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/next/schema", - "$ref": "https://json-schema.org/draft/next/schema" - }, - "tests": [ - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft-next/bar", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/bar#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft-next/root", - "$ref": "http://localhost:1234/draft-next/nested.json#/$defs/B", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#", - "type": "integer" - } - } - } - } - }, - "valid": true - } - ] - }, - { - "description": "Unnormalized $ids are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/next/schema", - "$ref": "https://json-schema.org/draft/next/schema" - }, - "tests": [ - { - "description": "Unnormalized identifier", - "data": { - "$ref": "http://localhost:1234/draft-next/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment", - "data": { - "$ref": "http://localhost:1234/draft-next/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft-next/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - } - ] - } -] diff --git a/src/test/suite/tests/draft-next/oneOf.json b/src/test/suite/tests/draft-next/oneOf.json index e8c077131..840d1579d 100644 --- a/src/test/suite/tests/draft-next/oneOf.json +++ b/src/test/suite/tests/draft-next/oneOf.json @@ -220,7 +220,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "$schema": "https://json-schema.org/draft/next/schema", diff --git a/src/test/suite/tests/draft-next/optional/dynamicRef.json b/src/test/suite/tests/draft-next/optional/dynamicRef.json new file mode 100644 index 000000000..dcace154e --- /dev/null +++ b/src/test/suite/tests/draft-next/optional/dynamicRef.json @@ -0,0 +1,56 @@ +[ + { + "description": "$dynamicRef skips over intermediate resources - pointer reference across resource boundary", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "$id": "https://test.json-schema.org/dynamic-ref-skips-intermediate-resource/optional/main", + "type": "object", + "properties": { + "bar-item": { + "$ref": "bar#/$defs/item" + } + }, + "$defs": { + "bar": { + "$id": "bar", + "type": "array", + "items": { + "$ref": "item" + }, + "$defs": { + "item": { + "$id": "item", + "type": "object", + "properties": { + "content": { + "$dynamicRef": "#content" + } + }, + "$defs": { + "defaultContent": { + "$dynamicAnchor": "content", + "type": "integer" + } + } + }, + "content": { + "$dynamicAnchor": "content", + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "integer property passes", + "data": { "bar-item": { "content": 42 } }, + "valid": true + }, + { + "description": "string property fails", + "data": { "bar-item": { "content": "value" } }, + "valid": false + } + ] + }] \ No newline at end of file diff --git a/src/test/suite/tests/draft-next/optional/ecmascript-regex.json b/src/test/suite/tests/draft-next/optional/ecmascript-regex.json index c96d4e052..a1a4f9638 100644 --- a/src/test/suite/tests/draft-next/optional/ecmascript-regex.json +++ b/src/test/suite/tests/draft-next/optional/ecmascript-regex.json @@ -405,20 +405,6 @@ } ] }, - { - "description": "\\a is not an ECMA 262 control escape", - "schema": { - "$schema": "https://json-schema.org/draft/next/schema", - "format": "regex" - }, - "tests": [ - { - "description": "when used as a pattern", - "data": "\\a", - "valid": false - } - ] - }, { "description": "pattern with non-ASCII digits", "schema": { diff --git a/src/test/suite/tests/draft-next/optional/format/duration.json b/src/test/suite/tests/draft-next/optional/format/duration.json index d5adca206..c4aa66bae 100644 --- a/src/test/suite/tests/draft-next/optional/format/duration.json +++ b/src/test/suite/tests/draft-next/optional/format/duration.json @@ -46,6 +46,11 @@ "data": "PT1D", "valid": false }, + { + "description": "must start with P", + "data": "4DT12H30M5S", + "valid": false + }, { "description": "no elements present", "data": "P", diff --git a/src/test/suite/tests/draft-next/optional/format/ecmascript-regex.json b/src/test/suite/tests/draft-next/optional/format/ecmascript-regex.json new file mode 100644 index 000000000..1e19c2729 --- /dev/null +++ b/src/test/suite/tests/draft-next/optional/format/ecmascript-regex.json @@ -0,0 +1,16 @@ +[ + { + "description": "\\a is not an ECMA 262 control escape", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "format": "regex" + }, + "tests": [ + { + "description": "when used as a pattern", + "data": "\\a", + "valid": false + } + ] + } +] diff --git a/src/test/suite/tests/draft-next/optional/format/email.json b/src/test/suite/tests/draft-next/optional/format/email.json index 5948ebd18..70633f188 100644 --- a/src/test/suite/tests/draft-next/optional/format/email.json +++ b/src/test/suite/tests/draft-next/optional/format/email.json @@ -115,6 +115,16 @@ "description": "an invalid IPv4-address-literal", "data": "joe.bloggs@[127.0.0.300]", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft-next/optional/format/hostname.json b/src/test/suite/tests/draft-next/optional/format/hostname.json index bfb306363..bc3a60dcc 100644 --- a/src/test/suite/tests/draft-next/optional/format/hostname.json +++ b/src/test/suite/tests/draft-next/optional/format/hostname.json @@ -120,6 +120,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft-next/optional/format/idn-hostname.json b/src/test/suite/tests/draft-next/optional/format/idn-hostname.json index 109bf73c9..1061f4243 100644 --- a/src/test/suite/tests/draft-next/optional/format/idn-hostname.json +++ b/src/test/suite/tests/draft-next/optional/format/idn-hostname.json @@ -257,7 +257,7 @@ { "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits", "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8", - "data": "\u0660\u06f0", + "data": "\u0628\u0660\u06f0", "valid": false }, { @@ -326,6 +326,63 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + } + ] + }, + { + "description": "validation of separators in internationalized host names", + "specification": [ + {"rfc3490": "3.1", "quote": "Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61(halfwidth ideographic full stop)"} + ], + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "format": "idn-hostname" + }, + "tests": [ + { + "description": "single dot", + "data": ".", + "valid": false + }, + { + "description": "single ideographic full stop", + "data": "\u3002", + "valid": false + }, + { + "description": "single fullwidth full stop", + "data": "\uff0e", + "valid": false + }, + { + "description": "single halfwidth ideographic full stop", + "data": "\uff61", + "valid": false + }, + { + "description": "dot as label separator", + "data": "a.b", + "valid": true + }, + { + "description": "ideographic full stop as label separator", + "data": "a\u3002b", + "valid": true + }, + { + "description": "fullwidth full stop as label separator", + "data": "a\uff0eb", + "valid": true + }, + { + "description": "halfwidth ideographic full stop as label separator", + "data": "a\uff61b", + "valid": true } ] } diff --git a/src/test/suite/tests/draft-next/unevaluatedItems.json b/src/test/suite/tests/draft-next/unevaluatedItems.json index 08f6ef128..6a728f8ae 100644 --- a/src/test/suite/tests/draft-next/unevaluatedItems.json +++ b/src/test/suite/tests/draft-next/unevaluatedItems.json @@ -711,6 +711,37 @@ } ] }, + { + "description" : "unevaluatedItems with minContains = 0", + "schema" : { + "$schema": "https://json-schema.org/draft/next/schema", + "contains": {"type": "string"}, + "minContains": 0, + "unevaluatedItems": false + }, + "tests" : [ + { + "description": "empty array is valid", + "data": [], + "valid": true + }, + { + "description": "no items evaluated by contains", + "data": [0], + "valid": false + }, + { + "description": "some but not all items evaluated by contains", + "data": ["foo", 0], + "valid": false + }, + { + "description": "all items evaluated by contains", + "data": ["foo", "bar"], + "valid": true + } + ] + }, { "description": "non-array instances are valid", "schema": { diff --git a/src/test/suite/tests/draft-next/unevaluatedProperties.json b/src/test/suite/tests/draft-next/unevaluatedProperties.json index d0d53507f..13fe6e03a 100644 --- a/src/test/suite/tests/draft-next/unevaluatedProperties.json +++ b/src/test/suite/tests/draft-next/unevaluatedProperties.json @@ -1603,5 +1603,74 @@ "valid": false } ] + }, + { + "description": "propertyDependencies with unevaluatedProperties" , + "schema" : { + "$schema": "https://json-schema.org/draft/next/schema", + "properties" : {"foo2" : {}}, + "propertyDependencies": { + "foo" : {}, + "foo2": { + "bar": { + "properties": { + "buz": {} + } + } + } + }, + "unevaluatedProperties": false + }, + + "tests": [ + { + "description": "unevaluatedProperties doesn't consider propertyDependencies" , + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "unevaluatedProperties sees buz when foo2 is present", + "data": {"foo2": "bar", "buz": ""}, + "valid": true + }, + { + "description": "unevaluatedProperties doesn't see buz when foo2 is absent", + "data": {"buz": ""}, + "valid": false + } + ] + }, + { + "description": "dependentSchemas with unevaluatedProperties", + "schema": { + "$schema": "https://json-schema.org/draft/next/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo" : {}, + "foo2": { + "properties": { + "bar":{} + } + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "unevaluatedProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties doesn't see bar when foo2 is absent", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties sees bar when foo2 is present", + "data": {"foo2": "", "bar": ""}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft2019-09/additionalProperties.json b/src/test/suite/tests/draft2019-09/additionalProperties.json index f9f03bb04..73f9b909e 100644 --- a/src/test/suite/tests/draft2019-09/additionalProperties.json +++ b/src/test/suite/tests/draft2019-09/additionalProperties.json @@ -152,5 +152,62 @@ "valid": true } ] + }, + { + "description": "additionalProperties with propertyNames", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "propertyNames": { + "maxLength": 5 + }, + "additionalProperties": { + "type": "number" + } + }, + "tests": [ + { + "description": "Valid against both keywords", + "data": { "apple": 4 }, + "valid": true + }, + { + "description": "Valid against propertyNames, but not additionalProperties", + "data": { "fig": 2, "pear": "available" }, + "valid": false + } + ] + }, + { + "description": "dependentSchemas with additionalProperties", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo" : {}, + "foo2": { + "properties": { + "bar":{} + } + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "additionalProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar even when foo2 is present", + "data": { "foo2": "", "bar": ""}, + "valid": false + } + ] } ] diff --git a/src/test/suite/tests/draft2019-09/anchor.json b/src/test/suite/tests/draft2019-09/anchor.json index eb0a969a8..bce05e800 100644 --- a/src/test/suite/tests/draft2019-09/anchor.json +++ b/src/test/suite/tests/draft2019-09/anchor.json @@ -116,30 +116,5 @@ "valid": false } ] - }, - { - "description": "invalid anchors", - "comment": "Section 8.2.3", - "schema": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$ref": "https://json-schema.org/draft/2019-09/schema" - }, - "tests": [ - { - "description": "MUST start with a letter (and not #)", - "data": { "$anchor" : "#foo" }, - "valid": false - }, - { - "description": "JSON pointers are not valid", - "data": { "$anchor" : "/a/b" }, - "valid": false - }, - { - "description": "invalid with valid beginning", - "data": { "$anchor" : "foo#something" }, - "valid": false - } - ] } ] diff --git a/src/test/suite/tests/draft2019-09/id.json b/src/test/suite/tests/draft2019-09/id.json deleted file mode 100644 index 0ba313874..000000000 --- a/src/test/suite/tests/draft2019-09/id.json +++ /dev/null @@ -1,211 +0,0 @@ -[ - { - "description": "Invalid use of fragments in location-independent $id", - "schema": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$ref": "https://json-schema.org/draft/2019-09/schema" - }, - "tests": [ - { - "description": "Identifier name", - "data": { - "$ref": "#foo", - "$defs": { - "A": { - "$id": "#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name and no ref", - "data": { - "$defs": { - "A": { "$id": "#foo" } - } - }, - "valid": false - }, - { - "description": "Identifier path", - "data": { - "$ref": "#/a/b", - "$defs": { - "A": { - "$id": "#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2019-09/bar#foo", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/bar#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier path with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2019-09/bar#/a/b", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/bar#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2019-09/root", - "$ref": "http://localhost:1234/draft2019-09/nested.json#foo", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#foo", - "type": "integer" - } - } - } - } - }, - "valid": false - }, - { - "description": "Identifier path with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2019-09/root", - "$ref": "http://localhost:1234/draft2019-09/nested.json#/a/b", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#/a/b", - "type": "integer" - } - } - } - } - }, - "valid": false - } - ] - }, - { - "description": "Valid use of empty fragments in location-independent $id", - "comment": "These are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$ref": "https://json-schema.org/draft/2019-09/schema" - }, - "tests": [ - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2019-09/bar", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/bar#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2019-09/root", - "$ref": "http://localhost:1234/draft2019-09/nested.json#/$defs/B", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#", - "type": "integer" - } - } - } - } - }, - "valid": true - } - ] - }, - { - "description": "Unnormalized $ids are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$ref": "https://json-schema.org/draft/2019-09/schema" - }, - "tests": [ - { - "description": "Unnormalized identifier", - "data": { - "$ref": "http://localhost:1234/draft2019-09/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment", - "data": { - "$ref": "http://localhost:1234/draft2019-09/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2019-09/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - } - ] - } -] diff --git a/src/test/suite/tests/draft2019-09/oneOf.json b/src/test/suite/tests/draft2019-09/oneOf.json index 9b7a2204e..c27d4865c 100644 --- a/src/test/suite/tests/draft2019-09/oneOf.json +++ b/src/test/suite/tests/draft2019-09/oneOf.json @@ -220,7 +220,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "$schema": "https://json-schema.org/draft/2019-09/schema", diff --git a/src/test/suite/tests/draft2019-09/optional/format/duration.json b/src/test/suite/tests/draft2019-09/optional/format/duration.json index 00d5f47ae..2d515a64a 100644 --- a/src/test/suite/tests/draft2019-09/optional/format/duration.json +++ b/src/test/suite/tests/draft2019-09/optional/format/duration.json @@ -46,6 +46,11 @@ "data": "PT1D", "valid": false }, + { + "description": "must start with P", + "data": "4DT12H30M5S", + "valid": false + }, { "description": "no elements present", "data": "P", diff --git a/src/test/suite/tests/draft2019-09/optional/format/email.json b/src/test/suite/tests/draft2019-09/optional/format/email.json index e6acc32bb..ea161eb2d 100644 --- a/src/test/suite/tests/draft2019-09/optional/format/email.json +++ b/src/test/suite/tests/draft2019-09/optional/format/email.json @@ -80,6 +80,16 @@ "description": "two subsequent dots inside local part are not valid", "data": "te..st@example.com", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft2019-09/optional/format/hostname.json b/src/test/suite/tests/draft2019-09/optional/format/hostname.json index f3b7181c8..24bfdfc5a 100644 --- a/src/test/suite/tests/draft2019-09/optional/format/hostname.json +++ b/src/test/suite/tests/draft2019-09/optional/format/hostname.json @@ -120,6 +120,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json b/src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json index 072a6b08e..348c504c8 100644 --- a/src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json +++ b/src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json @@ -257,7 +257,7 @@ { "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits", "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8", - "data": "\u0660\u06f0", + "data": "\u0628\u0660\u06f0", "valid": false }, { @@ -326,6 +326,63 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + } + ] + }, + { + "description": "validation of separators in internationalized host names", + "specification": [ + {"rfc3490": "3.1", "quote": "Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61(halfwidth ideographic full stop)"} + ], + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "format": "idn-hostname" + }, + "tests": [ + { + "description": "single dot", + "data": ".", + "valid": false + }, + { + "description": "single ideographic full stop", + "data": "\u3002", + "valid": false + }, + { + "description": "single fullwidth full stop", + "data": "\uff0e", + "valid": false + }, + { + "description": "single halfwidth ideographic full stop", + "data": "\uff61", + "valid": false + }, + { + "description": "dot as label separator", + "data": "a.b", + "valid": true + }, + { + "description": "ideographic full stop as label separator", + "data": "a\u3002b", + "valid": true + }, + { + "description": "fullwidth full stop as label separator", + "data": "a\uff0eb", + "valid": true + }, + { + "description": "halfwidth ideographic full stop as label separator", + "data": "a\uff61b", + "valid": true } ] } diff --git a/src/test/suite/tests/draft2019-09/propertyNames.json b/src/test/suite/tests/draft2019-09/propertyNames.json index b7fecbf71..3b2bb23bb 100644 --- a/src/test/suite/tests/draft2019-09/propertyNames.json +++ b/src/test/suite/tests/draft2019-09/propertyNames.json @@ -111,5 +111,58 @@ "valid": true } ] + }, + { + "description": "propertyNames with const", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "propertyNames": {"const": "foo"} + }, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with enum", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "propertyNames": {"enum": ["foo", "bar"]} + }, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property foo and bar is valid", + "data": {"foo": 1, "bar": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"baz": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft2019-09/ref.json b/src/test/suite/tests/draft2019-09/ref.json index ea569908e..eff5305c3 100644 --- a/src/test/suite/tests/draft2019-09/ref.json +++ b/src/test/suite/tests/draft2019-09/ref.json @@ -791,21 +791,6 @@ } ] }, - { - "description": "URN base URI with f-component", - "schema": { - "$schema": "https://json-schema.org/draft/2019-09/schema", - "$comment": "RFC 8141 §2.3.3, but we don't allow fragments", - "$ref": "https://json-schema.org/draft/2019-09/schema" - }, - "tests": [ - { - "description": "is invalid", - "data": {"$id": "urn:example:foo-bar-baz-qux#somepart"}, - "valid": false - } - ] - }, { "description": "URN base URI with URN and JSON pointer ref", "schema": { @@ -1063,5 +1048,43 @@ "valid": false } ] - } + }, + { + "description": "$ref with $recursiveAnchor", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://example.com/schemas/unevaluated-items-are-disallowed", + "$ref": "/schemas/unevaluated-items-are-allowed", + "$recursiveAnchor": true, + "unevaluatedItems": false, + "$defs": { + "/schemas/unevaluated-items-are-allowed": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "/schemas/unevaluated-items-are-allowed", + "$recursiveAnchor": true, + "type": "array", + "items": [ + { + "type": "string" + }, + { + "$ref": "#" + } + ] + } + } + }, + "tests": [ + { + "description": "extra items allowed for inner arrays", + "data" : ["foo",["bar" , [] , 8]], + "valid": true + }, + { + "description": "extra items disallowed for root", + "data" : ["foo",["bar" , [] , 8], 8], + "valid": false + } + ] + } ] diff --git a/src/test/suite/tests/draft2019-09/unevaluatedProperties.json b/src/test/suite/tests/draft2019-09/unevaluatedProperties.json index 71c36dfa0..e8765112c 100644 --- a/src/test/suite/tests/draft2019-09/unevaluatedProperties.json +++ b/src/test/suite/tests/draft2019-09/unevaluatedProperties.json @@ -1567,5 +1567,38 @@ "valid": false } ] + }, + { + "description": "dependentSchemas with unevaluatedProperties", + "schema": { + "$schema": "https://json-schema.org/draft/2019-09/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo" : {}, + "foo2": { + "properties": { + "bar":{} + } + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "unevaluatedProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties doesn't see bar when foo2 is absent", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties sees bar when foo2 is present", + "data": { "foo2": "", "bar": ""}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft2020-12/additionalProperties.json b/src/test/suite/tests/draft2020-12/additionalProperties.json index 29e69c135..9618575e2 100644 --- a/src/test/suite/tests/draft2020-12/additionalProperties.json +++ b/src/test/suite/tests/draft2020-12/additionalProperties.json @@ -2,6 +2,7 @@ { "description": "additionalProperties being false does not allow other properties", + "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema. Boolean \"false\" forbids everything." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": {"foo": {}, "bar": {}}, @@ -43,6 +44,7 @@ }, { "description": "non-ASCII pattern with additionalProperties", + "specification": [ { "core":"10.3.2.3"} ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "patternProperties": {"^á": {}}, @@ -63,6 +65,7 @@ }, { "description": "additionalProperties with schema", + "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": {"foo": {}, "bar": {}}, @@ -87,8 +90,8 @@ ] }, { - "description": - "additionalProperties can exist by itself", + "description": "additionalProperties can exist by itself", + "specification": [ { "core":"10.3.2.3", "quote": "With no other applicator applying to object instances. This validates all the instance values irrespective of their property names" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": {"type": "boolean"} @@ -108,6 +111,7 @@ }, { "description": "additionalProperties are allowed by default", + "specification": [ { "core":"10.3.2.3", "quote": "Omitting this keyword has the same assertion behavior as an empty schema." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "properties": {"foo": {}, "bar": {}} @@ -122,6 +126,7 @@ }, { "description": "additionalProperties does not look in applicators", + "specification":[ { "core": "10.2", "quote": "Subschemas of applicator keywords evaluate the instance completely independently such that the results of one such subschema MUST NOT impact the results of sibling subschemas." } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "allOf": [ @@ -139,6 +144,7 @@ }, { "description": "additionalProperties with null valued instance properties", + "specification": [ { "core":"10.3.2.3" } ], "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": { @@ -152,5 +158,62 @@ "valid": true } ] + }, + { + "description": "additionalProperties with propertyNames", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "propertyNames": { + "maxLength": 5 + }, + "additionalProperties": { + "type": "number" + } + }, + "tests": [ + { + "description": "Valid against both keywords", + "data": { "apple": 4 }, + "valid": true + }, + { + "description": "Valid against propertyNames, but not additionalProperties", + "data": { "fig": 2, "pear": "available" }, + "valid": false + } + ] + }, + { + "description": "dependentSchemas with additionalProperties", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo" : {}, + "foo2": { + "properties": { + "bar": {} + } + } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "additionalProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "additionalProperties can't see bar even when foo2 is present", + "data": {"foo2": "", "bar": ""}, + "valid": false + } + ] } ] diff --git a/src/test/suite/tests/draft2020-12/anchor.json b/src/test/suite/tests/draft2020-12/anchor.json index 83a7166d7..99143fa11 100644 --- a/src/test/suite/tests/draft2020-12/anchor.json +++ b/src/test/suite/tests/draft2020-12/anchor.json @@ -116,30 +116,5 @@ "valid": false } ] - }, - { - "description": "invalid anchors", - "comment": "Section 8.2.2", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "MUST start with a letter (and not #)", - "data": { "$anchor" : "#foo" }, - "valid": false - }, - { - "description": "JSON pointers are not valid", - "data": { "$anchor" : "/a/b" }, - "valid": false - }, - { - "description": "invalid with valid beginning", - "data": { "$anchor" : "foo#something" }, - "valid": false - } - ] } ] diff --git a/src/test/suite/tests/draft2020-12/dynamicRef.json b/src/test/suite/tests/draft2020-12/dynamicRef.json index bff26ad61..ffa211ba2 100644 --- a/src/test/suite/tests/draft2020-12/dynamicRef.json +++ b/src/test/suite/tests/draft2020-12/dynamicRef.json @@ -756,5 +756,60 @@ "valid": false } ] + }, + { + "description": "$dynamicRef skips over intermediate resources - direct reference", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://test.json-schema.org/dynamic-ref-skips-intermediate-resource/main", + "type": "object", + "properties": { + "bar-item": { + "$ref": "item" + } + }, + "$defs": { + "bar": { + "$id": "bar", + "type": "array", + "items": { + "$ref": "item" + }, + "$defs": { + "item": { + "$id": "item", + "type": "object", + "properties": { + "content": { + "$dynamicRef": "#content" + } + }, + "$defs": { + "defaultContent": { + "$dynamicAnchor": "content", + "type": "integer" + } + } + }, + "content": { + "$dynamicAnchor": "content", + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "integer property passes", + "data": { "bar-item": { "content": 42 } }, + "valid": true + }, + { + "description": "string property fails", + "data": { "bar-item": { "content": "value" } }, + "valid": false + } + ] } ] diff --git a/src/test/suite/tests/draft2020-12/id.json b/src/test/suite/tests/draft2020-12/id.json deleted file mode 100644 index 59265c4ec..000000000 --- a/src/test/suite/tests/draft2020-12/id.json +++ /dev/null @@ -1,211 +0,0 @@ -[ - { - "description": "Invalid use of fragments in location-independent $id", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "Identifier name", - "data": { - "$ref": "#foo", - "$defs": { - "A": { - "$id": "#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name and no ref", - "data": { - "$defs": { - "A": { "$id": "#foo" } - } - }, - "valid": false - }, - { - "description": "Identifier path", - "data": { - "$ref": "#/a/b", - "$defs": { - "A": { - "$id": "#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2020-12/bar#foo", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/bar#foo", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier path with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2020-12/bar#/a/b", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/bar#/a/b", - "type": "integer" - } - } - }, - "valid": false - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2020-12/root", - "$ref": "http://localhost:1234/draft2020-12/nested.json#foo", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#foo", - "type": "integer" - } - } - } - } - }, - "valid": false - }, - { - "description": "Identifier path with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2020-12/root", - "$ref": "http://localhost:1234/draft2020-12/nested.json#/a/b", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#/a/b", - "type": "integer" - } - } - } - } - }, - "valid": false - } - ] - }, - { - "description": "Valid use of empty fragments in location-independent $id", - "comment": "These are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "Identifier name with absolute URI", - "data": { - "$ref": "http://localhost:1234/draft2020-12/bar", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/bar#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Identifier name with base URI change in subschema", - "data": { - "$id": "http://localhost:1234/draft2020-12/root", - "$ref": "http://localhost:1234/draft2020-12/nested.json#/$defs/B", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$id": "#", - "type": "integer" - } - } - } - } - }, - "valid": true - } - ] - }, - { - "description": "Unnormalized $ids are allowed but discouraged", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "Unnormalized identifier", - "data": { - "$ref": "http://localhost:1234/draft2020-12/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/foo/bar/../baz", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment", - "data": { - "$ref": "http://localhost:1234/draft2020-12/foo/baz", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - }, - { - "description": "Unnormalized identifier with empty fragment and no ref", - "data": { - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/foo/bar/../baz#", - "type": "integer" - } - } - }, - "valid": true - } - ] - } -] diff --git a/src/test/suite/tests/draft2020-12/oneOf.json b/src/test/suite/tests/draft2020-12/oneOf.json index 416c8e570..7a7c7ffe3 100644 --- a/src/test/suite/tests/draft2020-12/oneOf.json +++ b/src/test/suite/tests/draft2020-12/oneOf.json @@ -220,7 +220,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/src/test/suite/tests/draft2020-12/optional/dynamicRef.json b/src/test/suite/tests/draft2020-12/optional/dynamicRef.json new file mode 100644 index 000000000..7e63f209a --- /dev/null +++ b/src/test/suite/tests/draft2020-12/optional/dynamicRef.json @@ -0,0 +1,56 @@ +[ + { + "description": "$dynamicRef skips over intermediate resources - pointer reference across resource boundary", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://test.json-schema.org/dynamic-ref-skips-intermediate-resource/optional/main", + "type": "object", + "properties": { + "bar-item": { + "$ref": "bar#/$defs/item" + } + }, + "$defs": { + "bar": { + "$id": "bar", + "type": "array", + "items": { + "$ref": "item" + }, + "$defs": { + "item": { + "$id": "item", + "type": "object", + "properties": { + "content": { + "$dynamicRef": "#content" + } + }, + "$defs": { + "defaultContent": { + "$dynamicAnchor": "content", + "type": "integer" + } + } + }, + "content": { + "$dynamicAnchor": "content", + "type": "string" + } + } + } + } + }, + "tests": [ + { + "description": "integer property passes", + "data": { "bar-item": { "content": 42 } }, + "valid": true + }, + { + "description": "string property fails", + "data": { "bar-item": { "content": "value" } }, + "valid": false + } + ] + }] \ No newline at end of file diff --git a/src/test/suite/tests/draft2020-12/optional/ecmascript-regex.json b/src/test/suite/tests/draft2020-12/optional/ecmascript-regex.json index 976495312..a4d62e0cf 100644 --- a/src/test/suite/tests/draft2020-12/optional/ecmascript-regex.json +++ b/src/test/suite/tests/draft2020-12/optional/ecmascript-regex.json @@ -405,20 +405,6 @@ } ] }, - { - "description": "\\a is not an ECMA 262 control escape", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "format": "regex" - }, - "tests": [ - { - "description": "when used as a pattern", - "data": "\\a", - "valid": false - } - ] - }, { "description": "pattern with non-ASCII digits", "schema": { diff --git a/src/test/suite/tests/draft2020-12/optional/format/duration.json b/src/test/suite/tests/draft2020-12/optional/format/duration.json index a3af56ef0..a09fec5ef 100644 --- a/src/test/suite/tests/draft2020-12/optional/format/duration.json +++ b/src/test/suite/tests/draft2020-12/optional/format/duration.json @@ -46,6 +46,11 @@ "data": "PT1D", "valid": false }, + { + "description": "must start with P", + "data": "4DT12H30M5S", + "valid": false + }, { "description": "no elements present", "data": "P", diff --git a/src/test/suite/tests/draft2020-12/optional/format/ecmascript-regex.json b/src/test/suite/tests/draft2020-12/optional/format/ecmascript-regex.json new file mode 100644 index 000000000..b0648084a --- /dev/null +++ b/src/test/suite/tests/draft2020-12/optional/format/ecmascript-regex.json @@ -0,0 +1,16 @@ +[ + { + "description": "\\a is not an ECMA 262 control escape", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "format": "regex" + }, + "tests": [ + { + "description": "when used as a pattern", + "data": "\\a", + "valid": false + } + ] + } +] \ No newline at end of file diff --git a/src/test/suite/tests/draft2020-12/optional/format/email.json b/src/test/suite/tests/draft2020-12/optional/format/email.json index ee075893c..925c2f2e3 100644 --- a/src/test/suite/tests/draft2020-12/optional/format/email.json +++ b/src/test/suite/tests/draft2020-12/optional/format/email.json @@ -115,6 +115,16 @@ "description": "an invalid IPv4-address-literal", "data": "joe.bloggs@[127.0.0.300]", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft2020-12/optional/format/hostname.json b/src/test/suite/tests/draft2020-12/optional/format/hostname.json index 41418dd4a..57827c4d4 100644 --- a/src/test/suite/tests/draft2020-12/optional/format/hostname.json +++ b/src/test/suite/tests/draft2020-12/optional/format/hostname.json @@ -120,6 +120,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json b/src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json index bc7d92f66..f42ae969b 100644 --- a/src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json +++ b/src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json @@ -257,7 +257,7 @@ { "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits", "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8", - "data": "\u0660\u06f0", + "data": "\u0628\u0660\u06f0", "valid": false }, { @@ -326,6 +326,63 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + } + ] + }, + { + "description": "validation of separators in internationalized host names", + "specification": [ + {"rfc3490": "3.1", "quote": "Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61(halfwidth ideographic full stop)"} + ], + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "format": "idn-hostname" + }, + "tests": [ + { + "description": "single dot", + "data": ".", + "valid": false + }, + { + "description": "single ideographic full stop", + "data": "\u3002", + "valid": false + }, + { + "description": "single fullwidth full stop", + "data": "\uff0e", + "valid": false + }, + { + "description": "single halfwidth ideographic full stop", + "data": "\uff61", + "valid": false + }, + { + "description": "dot as label separator", + "data": "a.b", + "valid": true + }, + { + "description": "ideographic full stop as label separator", + "data": "a\u3002b", + "valid": true + }, + { + "description": "fullwidth full stop as label separator", + "data": "a\uff0eb", + "valid": true + }, + { + "description": "halfwidth ideographic full stop as label separator", + "data": "a\uff61b", + "valid": true } ] } diff --git a/src/test/suite/tests/draft2020-12/propertyNames.json b/src/test/suite/tests/draft2020-12/propertyNames.json index 7ecfb7ec3..b4780088a 100644 --- a/src/test/suite/tests/draft2020-12/propertyNames.json +++ b/src/test/suite/tests/draft2020-12/propertyNames.json @@ -44,6 +44,36 @@ } ] }, + { + "description": "propertyNames validation with pattern", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "propertyNames": { "pattern": "^a+$" } + }, + "tests": [ + { + "description": "matching property names valid", + "data": { + "a": {}, + "aa": {}, + "aaa": {} + }, + "valid": true + }, + { + "description": "non-matching property name is invalid", + "data": { + "aaA": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + } + ] + }, { "description": "propertyNames with boolean schema true", "schema": { @@ -81,5 +111,58 @@ "valid": true } ] + }, + { + "description": "propertyNames with const", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "propertyNames": {"const": "foo"} + }, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with enum", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "propertyNames": {"enum": ["foo", "bar"]} + }, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property foo and bar is valid", + "data": {"foo": 1, "bar": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"baz": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft2020-12/ref.json b/src/test/suite/tests/draft2020-12/ref.json index 8d15fa43a..a1d3efaf7 100644 --- a/src/test/suite/tests/draft2020-12/ref.json +++ b/src/test/suite/tests/draft2020-12/ref.json @@ -791,21 +791,6 @@ } ] }, - { - "description": "URN base URI with f-component", - "schema": { - "$comment": "RFC 8141 §2.3.3, but we don't allow fragments", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "is invalid", - "data": {"$id": "urn:example:foo-bar-baz-qux#somepart"}, - "valid": false - } - ] - }, { "description": "URN base URI with URN and JSON pointer ref", "schema": { diff --git a/src/test/suite/tests/draft2020-12/unevaluatedItems.json b/src/test/suite/tests/draft2020-12/unevaluatedItems.json index ee0cb6586..9614c5855 100644 --- a/src/test/suite/tests/draft2020-12/unevaluatedItems.json +++ b/src/test/suite/tests/draft2020-12/unevaluatedItems.json @@ -718,6 +718,37 @@ } ] }, + { + "description" : "unevaluatedItems with minContains = 0", + "schema" : { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "contains": {"type": "string"}, + "minContains": 0, + "unevaluatedItems": false + }, + "tests" : [ + { + "description": "empty array is valid", + "data": [], + "valid": true + }, + { + "description": "no items evaluated by contains", + "data": [0], + "valid": false + }, + { + "description": "some but not all items evaluated by contains", + "data": ["foo", 0], + "valid": false + }, + { + "description": "all items evaluated by contains", + "data": ["foo", "bar"], + "valid": true + } + ] + }, { "description": "non-array instances are valid", "schema": { @@ -793,7 +824,6 @@ "data": [ "b" ], "valid": false } - ] } ] diff --git a/src/test/suite/tests/draft2020-12/unevaluatedProperties.json b/src/test/suite/tests/draft2020-12/unevaluatedProperties.json index b8a2306ca..0da38f679 100644 --- a/src/test/suite/tests/draft2020-12/unevaluatedProperties.json +++ b/src/test/suite/tests/draft2020-12/unevaluatedProperties.json @@ -3,7 +3,6 @@ "description": "unevaluatedProperties true", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "unevaluatedProperties": true }, "tests": [ @@ -25,7 +24,6 @@ "description": "unevaluatedProperties schema", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "unevaluatedProperties": { "type": "string", "minLength": 3 @@ -57,7 +55,6 @@ "description": "unevaluatedProperties false", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "unevaluatedProperties": false }, "tests": [ @@ -79,7 +76,6 @@ "description": "unevaluatedProperties with adjacent properties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -107,7 +103,6 @@ "description": "unevaluatedProperties with adjacent patternProperties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "patternProperties": { "^foo": { "type": "string" } }, @@ -132,13 +127,9 @@ ] }, { - "description": "unevaluatedProperties with adjacent additionalProperties", + "description": "unevaluatedProperties with adjacent bool additionalProperties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, "additionalProperties": true, "unevaluatedProperties": false }, @@ -160,11 +151,35 @@ } ] }, + { + "description": "unevaluatedProperties with adjacent non-bool additionalProperties", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": { "type": "string" }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with only valid additional properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with invalid additional properties", + "data": { + "foo": "foo", + "bar": 1 + }, + "valid": false + } + ] + }, { "description": "unevaluatedProperties with nested properties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -201,7 +216,6 @@ "description": "unevaluatedProperties with nested patternProperties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -238,7 +252,6 @@ "description": "unevaluatedProperties with nested additionalProperties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -271,7 +284,6 @@ "description": "unevaluatedProperties with nested unevaluatedProperties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -307,7 +319,6 @@ "description": "unevaluatedProperties with anyOf", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -376,7 +387,6 @@ "description": "unevaluatedProperties with oneOf", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -420,7 +430,6 @@ "description": "unevaluatedProperties with not", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -449,7 +458,6 @@ "description": "unevaluatedProperties with if/then/else", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "if": { "properties": { "foo": { "const": "then" } @@ -509,7 +517,6 @@ "description": "unevaluatedProperties with if/then/else, then not defined", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "if": { "properties": { "foo": { "const": "then" } @@ -563,7 +570,6 @@ "description": "unevaluatedProperties with if/then/else, else not defined", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "if": { "properties": { "foo": { "const": "then" } @@ -617,7 +623,6 @@ "description": "unevaluatedProperties with dependentSchemas", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -653,7 +658,6 @@ "description": "unevaluatedProperties with boolean schemas", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -681,7 +685,6 @@ "description": "unevaluatedProperties with $ref", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "$ref": "#/$defs/bar", "properties": { "foo": { "type": "string" } @@ -719,7 +722,6 @@ "description": "unevaluatedProperties before $ref", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "unevaluatedProperties": false, "properties": { "foo": { "type": "string" } @@ -773,7 +775,6 @@ "$comment": "unevaluatedProperties comes first so it's more likely to catch bugs with implementations that are sensitive to keyword ordering", "unevaluatedProperties": false, - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -862,7 +863,6 @@ "description": "nested unevaluatedProperties, outer false, inner true, properties outside", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -895,7 +895,6 @@ "description": "nested unevaluatedProperties, outer false, inner true, properties inside", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "properties": { @@ -928,7 +927,6 @@ "description": "nested unevaluatedProperties, outer true, inner false, properties outside", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { "type": "string" } }, @@ -961,7 +959,6 @@ "description": "nested unevaluatedProperties, outer true, inner false, properties inside", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "properties": { @@ -994,7 +991,6 @@ "description": "cousin unevaluatedProperties, true and false, true with properties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "properties": { @@ -1029,7 +1025,6 @@ "description": "cousin unevaluatedProperties, true and false, false with properties", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "unevaluatedProperties": true @@ -1065,10 +1060,8 @@ "comment": "see https://stackoverflow.com/questions/66936884/deeply-nested-unevaluatedproperties-and-their-expectations", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "foo": { - "type": "object", "properties": { "bar": { "type": "string" @@ -1117,7 +1110,6 @@ "description": "in-place applicator siblings, allOf has unevaluated", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "properties": { @@ -1163,7 +1155,6 @@ "description": "in-place applicator siblings, anyOf has unevaluated", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "allOf": [ { "properties": { @@ -1209,7 +1200,6 @@ "description": "unevaluatedProperties + single cyclic ref", "schema": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", "properties": { "x": { "$ref": "#" } }, @@ -1564,5 +1554,38 @@ "valid": false } ] + }, + { + "description": "dependentSchemas with unevaluatedProperties", + "schema": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "properties": {"foo2": {}}, + "dependentSchemas": { + "foo" : {}, + "foo2": { + "properties": { + "bar":{} + } + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "unevaluatedProperties doesn't consider dependentSchemas", + "data": {"foo": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties doesn't see bar when foo2 is absent", + "data": {"bar": ""}, + "valid": false + }, + { + "description": "unevaluatedProperties sees bar when foo2 is present", + "data": { "foo2": "", "bar": ""}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft3/optional/ecmascript-regex.json b/src/test/suite/tests/draft3/optional/format/ecmascript-regex.json similarity index 100% rename from src/test/suite/tests/draft3/optional/ecmascript-regex.json rename to src/test/suite/tests/draft3/optional/format/ecmascript-regex.json diff --git a/src/test/suite/tests/draft3/optional/format/email.json b/src/test/suite/tests/draft3/optional/format/email.json index 059615add..4749100a9 100644 --- a/src/test/suite/tests/draft3/optional/format/email.json +++ b/src/test/suite/tests/draft3/optional/format/email.json @@ -47,6 +47,16 @@ "description": "two subsequent dots inside local part are not valid", "data": "te..st@example.com", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft3/optional/format/host-name.json b/src/test/suite/tests/draft3/optional/format/host-name.json index d418f3763..9a75c3c20 100644 --- a/src/test/suite/tests/draft3/optional/format/host-name.json +++ b/src/test/suite/tests/draft3/optional/format/host-name.json @@ -57,6 +57,11 @@ "description": "exceeds maximum label length", "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", "valid": false + }, + { + "description": "empty string", + "data": "", + "valid": false } ] } diff --git a/src/test/suite/tests/draft3/refRemote.json b/src/test/suite/tests/draft3/refRemote.json index 0e4ab53e0..81a6c5116 100644 --- a/src/test/suite/tests/draft3/refRemote.json +++ b/src/test/suite/tests/draft3/refRemote.json @@ -17,7 +17,7 @@ }, { "description": "fragment within remote ref", - "schema": {"$ref": "http://localhost:1234/subSchemas.json#/definitions/integer"}, + "schema": {"$ref": "http://localhost:1234/draft3/subSchemas.json#/definitions/integer"}, "tests": [ { "description": "remote fragment valid", @@ -34,7 +34,7 @@ { "description": "ref within remote ref", "schema": { - "$ref": "http://localhost:1234/subSchemas.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft3/subSchemas.json#/definitions/refToInteger" }, "tests": [ { diff --git a/src/test/suite/tests/draft4/oneOf.json b/src/test/suite/tests/draft4/oneOf.json index fb63b0898..2487f0e38 100644 --- a/src/test/suite/tests/draft4/oneOf.json +++ b/src/test/suite/tests/draft4/oneOf.json @@ -159,7 +159,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "oneOf": [ diff --git a/src/test/suite/tests/draft4/optional/format/email.json b/src/test/suite/tests/draft4/optional/format/email.json index d6761a46b..84113f8a7 100644 --- a/src/test/suite/tests/draft4/optional/format/email.json +++ b/src/test/suite/tests/draft4/optional/format/email.json @@ -77,6 +77,16 @@ "description": "two subsequent dots inside local part are not valid", "data": "te..st@example.com", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft4/optional/format/hostname.json b/src/test/suite/tests/draft4/optional/format/hostname.json index a8ecd194f..866a61788 100644 --- a/src/test/suite/tests/draft4/optional/format/hostname.json +++ b/src/test/suite/tests/draft4/optional/format/hostname.json @@ -112,6 +112,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft4/refRemote.json b/src/test/suite/tests/draft4/refRemote.json index 64a618b89..65e45190c 100644 --- a/src/test/suite/tests/draft4/refRemote.json +++ b/src/test/suite/tests/draft4/refRemote.json @@ -17,7 +17,7 @@ }, { "description": "fragment within remote ref", - "schema": {"$ref": "http://localhost:1234/subSchemas.json#/definitions/integer"}, + "schema": {"$ref": "http://localhost:1234/draft4/subSchemas.json#/definitions/integer"}, "tests": [ { "description": "remote fragment valid", @@ -34,7 +34,7 @@ { "description": "ref within remote ref", "schema": { - "$ref": "http://localhost:1234/subSchemas.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft4/subSchemas.json#/definitions/refToInteger" }, "tests": [ { @@ -139,7 +139,7 @@ "id": "http://localhost:1234/object", "type": "object", "properties": { - "name": {"$ref": "name.json#/definitions/orNull"} + "name": {"$ref": "draft4/name.json#/definitions/orNull"} } }, "tests": [ @@ -171,7 +171,7 @@ { "description": "Location-independent identifier in remote ref", "schema": { - "$ref": "http://localhost:1234/locationIndependentIdentifierDraft4.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft4/locationIndependentIdentifier.json#/definitions/refToInteger" }, "tests": [ { diff --git a/src/test/suite/tests/draft6/oneOf.json b/src/test/suite/tests/draft6/oneOf.json index eeb7ae866..c30a65c0d 100644 --- a/src/test/suite/tests/draft6/oneOf.json +++ b/src/test/suite/tests/draft6/oneOf.json @@ -203,7 +203,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "oneOf": [ diff --git a/src/test/suite/tests/draft6/optional/format/email.json b/src/test/suite/tests/draft6/optional/format/email.json index d6761a46b..84113f8a7 100644 --- a/src/test/suite/tests/draft6/optional/format/email.json +++ b/src/test/suite/tests/draft6/optional/format/email.json @@ -77,6 +77,16 @@ "description": "two subsequent dots inside local part are not valid", "data": "te..st@example.com", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft6/optional/format/hostname.json b/src/test/suite/tests/draft6/optional/format/hostname.json index a8ecd194f..866a61788 100644 --- a/src/test/suite/tests/draft6/optional/format/hostname.json +++ b/src/test/suite/tests/draft6/optional/format/hostname.json @@ -112,6 +112,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft6/propertyNames.json b/src/test/suite/tests/draft6/propertyNames.json index f0788e649..7c7b80006 100644 --- a/src/test/suite/tests/draft6/propertyNames.json +++ b/src/test/suite/tests/draft6/propertyNames.json @@ -103,5 +103,52 @@ "valid": true } ] + }, + { + "description": "propertyNames with const", + "schema": {"propertyNames": {"const": "foo"}}, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with enum", + "schema": {"propertyNames": {"enum": ["foo", "bar"]}}, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property foo and bar is valid", + "data": {"foo": 1, "bar": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"baz": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft6/refRemote.json b/src/test/suite/tests/draft6/refRemote.json index 28459c4a0..49ead6d1f 100644 --- a/src/test/suite/tests/draft6/refRemote.json +++ b/src/test/suite/tests/draft6/refRemote.json @@ -17,7 +17,7 @@ }, { "description": "fragment within remote ref", - "schema": {"$ref": "http://localhost:1234/subSchemas.json#/definitions/integer"}, + "schema": {"$ref": "http://localhost:1234/draft6/subSchemas.json#/definitions/integer"}, "tests": [ { "description": "remote fragment valid", @@ -34,7 +34,7 @@ { "description": "ref within remote ref", "schema": { - "$ref": "http://localhost:1234/subSchemas.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft6/subSchemas.json#/definitions/refToInteger" }, "tests": [ { @@ -139,7 +139,7 @@ "$id": "http://localhost:1234/object", "type": "object", "properties": { - "name": {"$ref": "name.json#/definitions/orNull"} + "name": {"$ref": "draft6/name.json#/definitions/orNull"} } }, "tests": [ @@ -173,7 +173,7 @@ "schema": { "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json", "allOf": [ - { "$ref": "ref-and-definitions.json" } + { "$ref": "draft6/ref-and-definitions.json" } ] }, "tests": [ @@ -196,7 +196,7 @@ { "description": "Location-independent identifier in remote ref", "schema": { - "$ref": "http://localhost:1234/locationIndependentIdentifierPre2019.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft6/locationIndependentIdentifier.json#/definitions/refToInteger" }, "tests": [ { diff --git a/src/test/suite/tests/draft7/oneOf.json b/src/test/suite/tests/draft7/oneOf.json index eeb7ae866..c30a65c0d 100644 --- a/src/test/suite/tests/draft7/oneOf.json +++ b/src/test/suite/tests/draft7/oneOf.json @@ -203,7 +203,7 @@ } ] }, - { + { "description": "oneOf with missing optional property", "schema": { "oneOf": [ diff --git a/src/test/suite/tests/draft7/optional/format/email.json b/src/test/suite/tests/draft7/optional/format/email.json index d6761a46b..84113f8a7 100644 --- a/src/test/suite/tests/draft7/optional/format/email.json +++ b/src/test/suite/tests/draft7/optional/format/email.json @@ -77,6 +77,16 @@ "description": "two subsequent dots inside local part are not valid", "data": "te..st@example.com", "valid": false + }, + { + "description": "two email addresses is not valid", + "data": "user1@oceania.org, user2@oceania.org", + "valid": false + }, + { + "description": "full \"From\" header is invalid", + "data": "\"Winston Smith\" (Records Department)", + "valid": false } ] } diff --git a/src/test/suite/tests/draft7/optional/format/hostname.json b/src/test/suite/tests/draft7/optional/format/hostname.json index a8ecd194f..866a61788 100644 --- a/src/test/suite/tests/draft7/optional/format/hostname.json +++ b/src/test/suite/tests/draft7/optional/format/hostname.json @@ -112,6 +112,16 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + }, + { + "description": "single dot", + "data": ".", + "valid": false } ] } diff --git a/src/test/suite/tests/draft7/optional/format/idn-hostname.json b/src/test/suite/tests/draft7/optional/format/idn-hostname.json index dc47f7b5c..5c8cdc77b 100644 --- a/src/test/suite/tests/draft7/optional/format/idn-hostname.json +++ b/src/test/suite/tests/draft7/optional/format/idn-hostname.json @@ -254,7 +254,7 @@ { "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits", "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8", - "data": "\u0660\u06f0", + "data": "\u0628\u0660\u06f0", "valid": false }, { @@ -318,6 +318,60 @@ "description": "single label ending with digit", "data": "hostnam3", "valid": true + }, + { + "description": "empty string", + "data": "", + "valid": false + } + ] + }, + { + "description": "validation of separators in internationalized host names", + "specification": [ + {"rfc3490": "3.1", "quote": "Whenever dots are used as label separators, the following characters MUST be recognized as dots: U+002E (full stop), U+3002 (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61(halfwidth ideographic full stop)"} + ], + "schema": { "format": "idn-hostname" }, + "tests": [ + { + "description": "single dot", + "data": ".", + "valid": false + }, + { + "description": "single ideographic full stop", + "data": "\u3002", + "valid": false + }, + { + "description": "single fullwidth full stop", + "data": "\uff0e", + "valid": false + }, + { + "description": "single halfwidth ideographic full stop", + "data": "\uff61", + "valid": false + }, + { + "description": "dot as label separator", + "data": "a.b", + "valid": true + }, + { + "description": "ideographic full stop as label separator", + "data": "a\u3002b", + "valid": true + }, + { + "description": "fullwidth full stop as label separator", + "data": "a\uff0eb", + "valid": true + }, + { + "description": "halfwidth ideographic full stop as label separator", + "data": "a\uff61b", + "valid": true } ] } diff --git a/src/test/suite/tests/draft7/propertyNames.json b/src/test/suite/tests/draft7/propertyNames.json index f0788e649..7c7b80006 100644 --- a/src/test/suite/tests/draft7/propertyNames.json +++ b/src/test/suite/tests/draft7/propertyNames.json @@ -103,5 +103,52 @@ "valid": true } ] + }, + { + "description": "propertyNames with const", + "schema": {"propertyNames": {"const": "foo"}}, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with enum", + "schema": {"propertyNames": {"enum": ["foo", "bar"]}}, + "tests": [ + { + "description": "object with property foo is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property foo and bar is valid", + "data": {"foo": 1, "bar": 1}, + "valid": true + }, + { + "description": "object with any other property is invalid", + "data": {"baz": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] } ] diff --git a/src/test/suite/tests/draft7/refRemote.json b/src/test/suite/tests/draft7/refRemote.json index 22185d678..450787af6 100644 --- a/src/test/suite/tests/draft7/refRemote.json +++ b/src/test/suite/tests/draft7/refRemote.json @@ -17,7 +17,7 @@ }, { "description": "fragment within remote ref", - "schema": {"$ref": "http://localhost:1234/subSchemas.json#/definitions/integer"}, + "schema": {"$ref": "http://localhost:1234/draft7/subSchemas.json#/definitions/integer"}, "tests": [ { "description": "remote fragment valid", @@ -34,7 +34,7 @@ { "description": "ref within remote ref", "schema": { - "$ref": "http://localhost:1234/subSchemas.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft7/subSchemas.json#/definitions/refToInteger" }, "tests": [ { @@ -139,7 +139,7 @@ "$id": "http://localhost:1234/object", "type": "object", "properties": { - "name": {"$ref": "name.json#/definitions/orNull"} + "name": {"$ref": "draft7/name.json#/definitions/orNull"} } }, "tests": [ @@ -173,7 +173,7 @@ "schema": { "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json", "allOf": [ - { "$ref": "ref-and-definitions.json" } + { "$ref": "draft7/ref-and-definitions.json" } ] }, "tests": [ @@ -196,7 +196,7 @@ { "description": "Location-independent identifier in remote ref", "schema": { - "$ref": "http://localhost:1234/locationIndependentIdentifierPre2019.json#/definitions/refToInteger" + "$ref": "http://localhost:1234/draft7/locationIndependentIdentifier.json#/definitions/refToInteger" }, "tests": [ { diff --git a/src/test/suite/tests/latest b/src/test/suite/tests/latest new file mode 100644 index 000000000..9a4784dd7 --- /dev/null +++ b/src/test/suite/tests/latest @@ -0,0 +1 @@ +draft2020-12 \ No newline at end of file