Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
17 changes: 15 additions & 2 deletions src/main/java/com/networknt/schema/utils/RFC5892.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 '.'.

Expand Down
12 changes: 6 additions & 6 deletions src/test/java/com/networknt/schema/UriMappingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand All @@ -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(
Expand All @@ -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();
Expand All @@ -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());
}
Expand Down
2 changes: 2 additions & 0 deletions src/test/java/com/networknt/schema/suite/TestCase.java
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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 {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

]
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]
116 changes: 116 additions & 0 deletions src/test/suite/annotations/README.md
Original file line number Diff line number Diff line change
@@ -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.
24 changes: 24 additions & 0 deletions src/test/suite/annotations/assertion.schema.json
Original file line number Diff line number Diff line change
@@ -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"]
}
38 changes: 38 additions & 0 deletions src/test/suite/annotations/test-case.schema.json
Original file line number Diff line number Diff line change
@@ -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"]
}
15 changes: 15 additions & 0 deletions src/test/suite/annotations/test-suite.schema.json
Original file line number Diff line number Diff line change
@@ -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"]
}
16 changes: 16 additions & 0 deletions src/test/suite/annotations/test.schema.json
Original file line number Diff line number Diff line change
@@ -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"]
}
Loading