diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..e0bf6764 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,183 @@ +# Contributing to json_schema + +Looking to contribute something to the json_schema library? __Here's how you can help.__ + ++ __[Coding Standards](#coding-standards)__ + + [General Formatting Guidelines](#general-formatting-guidelines) ++ __[Using the Issue Tracker](#using-the-issue-tracker)__ + + [Reporting Bugs](#bug-reports) + + [Feature Requests](#feature-requests) + + [Submitting Pull Requests](#pull-requests) ++ __[Developer Workflow](#developer-workflow)__ + + + + +## Coding standards + +A lot can be gained by writing code in a consistent way. Moreover, always remember that code is written and +maintained by _people_. Ensure your code is descriptive, well commented, and approachable by others. + +__ALWAYS__ adhere to the [Dart Style Guide]. _Please take the time to read it if you have never done so._ + +  + + +### General formatting guidelines + ++ __AVOID__ lines longer than 120 characters. ++ __AVOID__ using `dartfmt` as an excuse to ignore good judgement about + whether your code is readable and approachable by others. + +  +  + + + +## Using the issue tracker + +The issue tracker is the preferred channel for [bug reports](#bug-reports) and [feature requests](#feature-requests), +but __please follow the guidelines:__ + + + __Fill out the template we've provided.__ + + + __Be Professional__ + + Please __do not__ derail or troll issues. Keep the discussion on topic and respect the opinions of others. + + + __Not that Professional__ + + Feel free to include _relevant_ animated gifs to drive home your message / request. + +  + + +### Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. + +_Good bug reports are extremely helpful - thank you!__ + +__Guidelines for bug reports:__ + +1. __Search for existing issues.__ Duplicate issues can become cumbersome, and you'd help us out a lot by first + checking if someone else has reported the same issue. Moreover, the issue may have already been resolved with a + fix available. + +2. __Record a screencast of yourself reproducing the issue__. + 1. Be sure the problem exists in json_schema's code by building a + reduced test case that one of the reviewers can pull locally + and test out. + +3. __Share as much information as possible.__ Include operating system and version, browser and version, etc. where appropriate. + +Always include steps to reproduce the bug. + +__Example Bug Report:__ + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to branch with the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + +  + + +### Feature requests + +Feature requests are welcome. But take a moment to find out whether your idea fits with the scope and aims of the +project. It's up to *you* to make a strong case to convince the `json_schema` team of the merits of this feature. +Please provide as much detail and context as possible. + +  + + +### Pull requests + +Good pull requests - patches, improvements, new features - are a fantastic help. They should remain focused in scope +and avoid containing unrelated commits. + +__Please ask first__ before embarking on any significant pull request (e.g. implementing features, refactoring code, +porting to a different language), otherwise you risk spending a lot of time working on something that the project's +lead developers might not want to merge into the project. + +Please adhere to the [Dart Style Guide] for all changes contained in your pull requests. + +Adhering to the following process is the best way to get your work included in the project: + +1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Navigate to the directory where you store repos locally + cd ~/your-local-git-repo-spot + # Clone your fork of the repo into the current directory + git clone git@github.com:/json_schema + # Navigate to the newly cloned directory + cd ~/your-local-git-repo-spot/json_schema + # Assign the repo you forked from to a remote called "upstream" + git remote add upstream git@github.com:Workiva/json_schema + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout master + git pull upstream master + ``` + +3. Create a new topic branch that will contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these + [git commit message guidelines](#git-commit-message-standards) or your code is unlikely be merged into the master + branch. Optionally, you can use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Write tests for your changes. + 1. There are no exceptions. + 2. If you're having trouble, reach out in your PR about how to best go about testing your changes. + +6. If you have merge conflicts, locally merge the upstream master branch into your topic branch: + + ```bash + git pull upstream master + ``` + +7. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description - following all the [issue guidelines](#using-the-issue-tracker) listed above. + +  +  + + +## Developer Workflow + +The `json_schema` developer workflow couldn't be any more simple! + +When you're ready to run the tests... run: + +```bash +pub run dart_dev test +``` + + +[Dart Style Guide]: https://www.dartlang.org/guides/language/effective-dart/style \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..f5db4250 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ + + + +## Type + +## Details + + + +## Possible Solution + + + + + +## Steps to Reproduce + +1. +2. +3. + +## Your Environment + +* Version: +* Browser name / VM and version: +* Operating System and version: + +> __FYI:__ @michaelcarter-wf \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..e2bcab9f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## Ultimate problem: + + +## How it was fixed: + + +## Testing suggestions: + + +## Potential areas of regression: + + + +--- + +> __FYA:__ @michaelcarter-wf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0de029e4..baa08082 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,16 @@ *.~*~ +.idea/ .packages -packages -build/ -.pub/ .project +.dart_tool/ +*.dart.js *.iml *.ipr *.iws -.idea/ -*.dart.js *.js_ *.js.deps *.js.map -# custom +build/ +coverage pubspec.lock -# end diff --git a/.travis.yml b/.travis.yml index 2e9f3e76..5e10d08a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,19 @@ language: dart -sudo: false dart: - stable - - dev -script: ./tool/travis.sh \ No newline at end of file +sudo: required +addons: + chrome: stable +cache: + directories: + - $HOME/.pub-cache +install: true +script: + - pub get + - pub run dependency_validator + - pub run dart_dev format --check + - pub run dart_dev analyze + - pub run dart_dev test -P travis + - pub run dart_dev test --test-args="--platform chrome" --release -P travis + # TODO: re-enable coverage when a Dart 2 solution is available. + # - pub run dart_dev dart1-only -- coverage --no-html && bash <(curl -s https://codecov.io/bash) -f coverage/coverage.lcov \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b90af2e1..07c657f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +## 3.0.0 + +* Removed support for Dart 1 + +## 2.2.0 + +* Add note about root path in error string when instance path is empty +* Expose `ValidationError` class + +## 2.1.4 + +* Use deep equality to compare maps, fixing equality when enums are present + +## 2.1.3 + +* New `validateWithErrors` method on `JsonSchema` returns all validation errors as a list of objects +* `ValidationError` objects include both instance & schema paths for each error +* Error logic tweaked to provide consistent error paths in JSON pointer notation + +## 2.0.0 + +* json_schema is no longer bound to dart:io and works in the browser! +* Full JSON Schema draft6 compatibility +* Much better $ref resolution, including deep nesting of $refs +* More typed keyword getters for draft6 like `examples` +* Syncronous schema evaluation by default +* Optional async evaluation and fetching with `createSchemaAsync` +* Automatic parsing of JSON strings passed to `createSchema` and `createSchemaAsync` +* Ability to do custom resolution of $refs with `RefProvider` and `RefProviderAsync` +* Optional parsing of JSON strings passed to `validate` with `parseJson = true` +* Dart 2.0 compatibility +* Many small changes to make things more in line with modern dart. +* Please see the [migration guide](./MIGRATION.md) for additional info. + +## 1.0.8 + +* Code cleanup +* Strong mode +* Switch build tools to dart_dev + ## 1.0.7 * Update dependency constraint on the `args` package. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..414cefe2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM drydock-prod.workiva.net/workiva/dart_build_image:1 as build + +FROM scratch diff --git a/LICENSE b/LICENSE index cd4d2bd6..9f3f33ef 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,37 @@ -License: Boost License 1.0. +Copyright 2013-2018 Workiva Inc. + +Licensed under the Boost Software License (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.boost.org/LICENSE_1_0.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This software or document includes material copied from or derived +from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +Copyright (c) 2012 Julian Berman, which is licensed under the following terms: + + Copyright (c) 2012 Julian Berman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/MIGRATION.md b/MIGRATION.md new file mode 100644 index 00000000..78d55a3a --- /dev/null +++ b/MIGRATION.md @@ -0,0 +1,60 @@ +# json_schema v2.x to v3 Migration Guide + +json_schema 3.0 is now here due to an issue that was found in 2.0 that caused remote refs to not get resolved correctly. This forced us to sort through the ref resolution logic in schema construction and change a few underlying assumptions. While on the surface it doesn't look like any publically exposed members or methods were modified or removed, the map of sub-properties' and references' [JsonSchema]s by path (`_refMap`) is structured differently to accommodate new logic changes in schema construction and resolution. + +All tests continue to pass and most consumers will not have to make any changes, but for some more advanced use cases, these changes might break schema resolution/validation which we prefer to protect against with a 3.0 major version upgrade. + + +# json_schema v1.x to v2 Migration Guide + +json_schema 2.0 is here, and is packed with useful updates! We've tried to minimize incompatibilities, while taking steps to build for the future. These include: + +- json_schema is no longer bound to dart:io! + - json_schema can now be used in either the browser or on a server by utilizing `configureJsonSchemaForBrowser()` or `configureBrowserForVm()`. Only necessary if you use async fetch of remote schemas via `createSchemaAsync`. +- JSON Schema draft6 compatibility + - Allowing multiple spec versions (draft4 and draft6) +- Synchronous evaluation of schemas, by default. + - Make fetching referenced schemas possible out-of-band and explicitly, while removing the default behavior to make HTTP calls. +- Renaming or splitting up certain keyword getters for better type-safety. + - i.e. `bool additionalPropertiesBool` and `JsonSchema additionalPropertiesSchema` vs `dynamic addtionalProperties`. +- Automatic parsing of JSON strings when they are passed to `createSchema`, for more straightforward creation of schemas +- Optional parsing of JSON strings when they are passed to `validate`. +- The repo is now maintained by Workiva. + + +## Breaking Changes + +- `Schema` --> `JsonSchema` + - We've changed the name of the main class, for clarity. + +- `JsonShema createSchema` + - DON'T PANIC, we've changed the signature of the main constructor! We did this in order to allow syncronous evaluation of schemas by default. + - There are a few paths you can take here: + - If you were using `createSchema` to evaluate schemas that contained remote references you don't have cached locally, simply switch over to `createSchemaAsync`, which has the same behavior. If you continue to use the synchronous `createSchema`: *errors will be thrown when remote references are encountered*. + - If you were using `createSchema` to evaluate schemas where all references can be resolved within the root schema, congrats! You can now remove all async behavior around creating schemas. No more async / await :) + - If you were using `createSchema` to evaluate schema which have remote references, but you can cache all the remote references locally, you can use the optional `RefProvider` to allow sync resolution of those. + - A new use case is also available: you can now use custom logic to resolve your own $refs using `RefProviderAsync` / `createSchemaAsync`. + +- Platforms + - dart:io Users: A single call to `configureBrowserForVm()` is required before using `createSchemaAsync`. + - dart:html Users: A single call to `configureBrowserForBrowser()` is required before using `createSchemaAsync`. + +- Removal of Custom Validation Logic + - `set uriValidator` and `set emailValidator` have been removed and replaced with spec-supplied logical constraints. + - This was removed because it was one-off for these two formats. Look for generic custom format validation in the future. + +- `exclusiveMaximum` and `exclusiveMinimum` + - changed `bool get exclusiveMinimum` --> `num get exclusiveMinimum` and + `bool get exclusiveMaximum` --> `num get exclusiveMaximum`. The old boolean values are available under `bool get hasExclusiveMinimum` and `bool get hasExclusiveMaximum`, while the new values contain the actual value of the min / max. This is consistent with how the spec was changes, see the release notes: https://json-schema.org/draft-06/json-schema-release-notes.html + +- `String get ref` --> `Uri get ref` + - Since the spec specifies that $refs MUST be a URI, we've given refs some additional type safety.(https://tools.ietf.org/html/draft-wright-json-schema-01#section-8). + +## Notable Deprecations + +- `JsonSchema.refMap` + - Note: This information is useful for drawing dependency graphs, etc, but should not be used for general +validation or traversal. Use `endPath` to get the absolute `String` path and `resolvePath` to get the `JsonSchema` at any path, instead. This functionality will be removed in 3.0. + +- All `schema_dot.dart` exports including `SchemaNode` and `createDot` + - Unfortunetly, we don't have the resources to maintain this part of the codebase, as such, it has been untested against the major changes in 2.0, and is marked for future removal. Raise an issue or submit a PR if this was important to you. \ No newline at end of file diff --git a/README.md b/README.md index 6539a3fa..9691f883 100644 --- a/README.md +++ b/README.md @@ -1,115 +1,351 @@ -# Json Schema +# JSON Schema - A *dart:io* dependent library for validating json instances against json schema (version Draft 04) + A *platform agnostic* (dart:html or dart:io) Dart library for validating JSON instances against JSON Schemas (multi-version support with latest of Draft 6). -![Build Status](https://travis-ci.org/patefacio/json_schema.svg) +![Build Status](https://travis-ci.org/workiva/json_schema.svg) +## How To Create and Validate Against a Schema + +### Synchronous Creation - Self Contained + +The simplest way to create a schema is to pass JSON data directly to `JsonSchema.createSchema` with a JSON `String`, or decoded JSON via Dart `Map` or `bool`. -# How To Validate +After creating any schema, JSON instances can be validated by calling `.validate(instance)` on that schema. By default, instances are expected to be pre-parsed JSON as native dart primitives (`Map`, `List`, `String`, `bool`, `num`, `int`). You can also optionally parse at validation time by passing in a string and setting `parseJson`: `schema.validate('{ "name": "any JSON object"}', parseJson: true)`. + + > Note: Creating JsonSchemas synchronously implies access to all $refs within the root schema. If you don't have access to all this data at the time of the construction, see "Asynchronous Creation" examples below. - To validate instances against a schema first create the schema, then - call validate on it with an json instance. This can be done with an - url: -### Example 1 +#### Example - String url = "http://json-schema.org/draft-04/schema"; - Schema.createSchemaFromUrl(url) - .then((schema) { - print('Does schema validate itself? ${schema.validate(schema.schemaMap)}'); - }); +A schema can be created with a Map that is either hand-crafted, referenced from a JSON file, or *previously* fetched from the network or file system. - In this example a schema is created from the url and its stored - contents are validated against itself. Since the referenced schema - is the schema for schemas and the instance is, of course, a schema, - the result prints true. +```dart +import 'package:json_schema/json_schema.dart'; -### Example 2 - - An url can point to a local file, either of format - _file:///absolute\_path\_to/schema.json_ or _subfolder/schema.json_ - where _subfolder_ is a subfolder of current working directory. An - example of this can be found in - _example/from\_url/validate\_instance\_from\_url.dart_ - - url = "grades_schema.json"; - Schema.createSchemaFromUrl(url) - .then((schema) { - var grades = JSON.parse(''' +main() { + /// Define schema in a Dart [Map] or use a JSON [String]. + final mustBeIntegerSchemaMap = {"type": "integer"}; + + // Create some examples to validate against the schema. + final n = 3; + final decimals = 3.14; + final str = 'hi'; + + // Construct the schema from the schema map or JSON string. + final schema = JsonSchema.createSchema(mustBeIntegerSchemaMap); + + print('$n => ${schema.validate(n)}'); // true + print('$decimals => ${schema.validate(decimals)}'); // false + print('$str => ${schema.validate(str)}'); // false +} +``` + +### Synchronous Creation, Local Ref Cache + +If you want to create `JsonSchema`s synchronously, and you have $refs that cannot be resolved within the root schema, but you have a cache of those $ref'd schemas locally, you can write a `RefProvider` to get them during schema evaluation. + +#### Example + +```dart +import 'package:json_schema/json_schema.dart'; +import 'package:dart2_constant/convert.dart'; + +main() { + final referencedSchema = { + r"$id": "https://example.com/geographical-location.schema.json", + r"$schema": "http://json-schema.org/draft-06/schema#", + "title": "Longitude and Latitude", + "description": "A geographical coordinate on a planet (most commonly Earth).", + "required": ["latitude", "longitude"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "latitude": {"type": "number", "minimum": -90, "maximum": 90}, + "longitude": {"type": "number", "minimum": -180, "maximum": 180} + } + }; + + final RefProvider refProvider = (String ref) { + final Map references = { + 'https://example.com/geographical-location.schema.json': JsonSchema.createSchema(referencedSchema), + }; + + if (references.containsKey(ref)) { + return references[ref]; + } + + return null; + }; + + final schema = JsonSchema.createSchema({ + 'type': 'array', + 'items': {r'$ref': 'https://example.com/geographical-location.schema.json'} + }, refProvider: refProvider); + + final workivaLocations = [ { - "semesters": [ - { - "semester": 1, - "grades": [ - { - "type": "homework", - "date": "09/27/2013", - "grade": 100, - "avg": 93, - "std": 8 - }, - { - "type": "homework", - "date": "09/28/2013", - "grade": 100, - "avg": 60, - "std": 25 - } - ] - } - ] - }'''); - - print('''Does grades schema validate $grades - ${schema.validate(grades)}'''); - - In this example the schema is read from file _grades\_schema.json_ - in the current directory and a valid instance is submitted for - validation (in the string of the print statement). This example also - prints true. - -### Example 3 - - A schema can be created with a Map that is either hand-crafted or - the result of a call to json parse. - - ////////////////////////////////////////////////////////////////////// - // Define schema in code - ////////////////////////////////////////////////////////////////////// - var mustBeIntegerSchema = { - "type" : "integer" - }; - - var n = 3; - var decimals = 3.14; - var str = 'hi'; - - Schema.createSchema(mustBeIntegerSchema) - .then((schema) { - print('$n => ${schema.validate(n)}'); - print('$decimals => ${schema.validate(decimals)}'); - print('$str => ${schema.validate(str)}'); - }); - - This example creates a schema requiring the type be integer. It then - tests against three instances with the following results: - - 3 => true - 3.14 => false - hi => false - -# How To Use Schema Information - - Schema information can be used for validation; but it can also be a - valuable source of information about the structure of data. The - Schema class provided here works by fully parsing the schema first, - which itself must be valid on all paths within the schema. The only - invalid content of a provided schema are _free-form properties_ - containing schema that are not referenced. Accessors are provided - for the meta-data associated with a schema, so tools can do *stuff* - with it. - - One example use is the _schemadot_ program included in the _bin_ + 'name': 'Ames', + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }, + { + 'name': 'Scottsdale', + 'latitude': 33.4634707, + 'longitude': -111.9266617, + } + ]; + + final badLocations = [ + { + 'name': 'Bad Badlands', + 'latitude': 181, + 'longitude': 92, + }, + { + 'name': 'Nowhereville', + 'latitude': -2000, + 'longitude': 7836, + } + ]; + + print('${json.encode(workivaLocations)} => ${schema.validate(workivaLocations)}'); + print('${json.encode(badLocations)} => ${schema.validate(badLocations)}'); +} +``` + +### Asynchronous Creation, Remote HTTP Refs + +If you have schemas that have nested $refs that are HTTP URIs that are publicly accessible, you can use `Future JsonSchema.createSchemaAsync` and the references will be fetched as needed during evaluation. You can also use `JsonSchema.createSchemaFromUrl` if you want to fetch the root schema remotely as well (see next example). + +#### Example + +```dart +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + // Schema Defined as a JSON String + final schema = await JsonSchema.createSchemaAsync(r''' + { + "type": "array", + "items": { + "$ref": "https://raw.githubusercontent.com/json-schema-org/JSON-Schema-Test-Suite/master/remotes/integer.json" + } + } + '''); + + // Create some examples to validate against the schema. + final numbersArray = [1, 2, 3]; + final decimalsArray = [3.14, 1.2, 5.8]; + final strArray = ['hello', 'world']; + + print('$numbersArray => ${schema.validate(numbersArray)}'); // true + print('$decimalsArray => ${schema.validate(decimalsArray)}'); // false + print('$strArray => ${schema.validate(strArray)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} +``` + +### Asynchronous Creation, From URL or File + +You can also create a schema directly from a publicly accessible URL, like so: + +#### Example 1 - URL + +```dart +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final url = "https://raw.githubusercontent.com/json-schema-org/JSON-Schema-Test-Suite/master/remotes/integer.json"; + + final schema = await JsonSchema.createSchemaFromUrl(url); + + // Create some examples to validate against the schema. + final n = 3; + final decimals = 3.14; + final str = 'hi'; + + print('$n => ${schema.validate(n)}'); // true + print('$decimals => ${schema.validate(decimals)}'); // false + print('$str => ${schema.validate(str)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} +``` + +#### Example 2 - File + +```dart +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final file = "example/readme/asynchronous_creation/geo.schema.json"; + + final schema = await JsonSchema.createSchemaFromUrl(file); + + // Create some examples to validate against the schema. + final workivaAmes = { + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }; + + final nowhereville = { + 'latitude': -2000, + 'longitude': 7836, + }; + + print('$workivaAmes => ${schema.validate(workivaAmes)}'); // true + print('$nowhereville => ${schema.validate(nowhereville)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} +``` + +### Asynchronous Creation, with custom remote $refs: + +If you have nested $refs that are either non-HTTP URIs or non-publicly-accessible HTTP $refs, you can supply an `RefProviderAsync` to `createSchemaAsync`, and perform any custom logic you need. + +#### Example + +```dart +import 'dart:io'; +import 'dart:async'; +import 'package:dart2_constant/convert.dart'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final referencedSchema = { + r"$id": "https://example.com/geographical-location.schema.json", + r"$schema": "http://json-schema.org/draft-06/schema#", + "title": "Longitude and Latitude", + "description": "A geographical coordinate on a planet (most commonly Earth).", + "required": ["latitude", "longitude"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "latitude": {"type": "number", "minimum": -90, "maximum": 90}, + "longitude": {"type": "number", "minimum": -180, "maximum": 180} + } + }; + + final RefProviderAsync refProvider = (String ref) async { + final Map references = { + 'https://example.com/geographical-location.schema.json': JsonSchema.createSchema(referencedSchema), + }; + + if (references.containsKey(ref)) { + // Silly example that adds a 1 second delay. + // In practice, you could make any service call here, + // parse the results into a schema, and return. + await new Future.delayed(new Duration(seconds: 1)); + return references[ref]; + } + + // Fall back to default URL $ref behavior + return await JsonSchema.createSchemaFromUrl(ref); + }; + + final schema = await JsonSchema.createSchemaAsync({ + 'type': 'array', + 'items': {r'$ref': 'https://example.com/geographical-location.schema.json'} + }, refProvider: refProvider); + + final workivaLocations = [ + { + 'name': 'Ames', + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }, + { + 'name': 'Scottsdale', + 'latitude': 33.4634707, + 'longitude': -111.9266617, + } + ]; + + final badLocations = [ + { + 'name': 'Bad Badlands', + 'latitude': 181, + 'longitude': 92, + }, + { + 'name': 'Nowhereville', + 'latitude': -2000, + 'longitude': 7836, + } + ]; + + print('${json.encode(workivaLocations)} => ${schema.validate(workivaLocations)}'); + print('${json.encode(badLocations)} => ${schema.validate(badLocations)}'); + + exit(0); +} +``` + +## How To Use Schema Information + + Schema information can be used for validation; but it can also be a valuable source of information about the structure of data. The `JsonSchema` class fully parses the schema first, which itself must be valid on all paths within the schema. Accessors are provided for all specified keywords of the JSON Schema specification associated with a schema, so tools can use it to create rich views of the data, like forms or diagrams. + + One example use is the *deprecated* _schemadot_ program included in the _bin_ folder which takes schema as input and outputs a _Graphviz_ _dot_ file, providing a picture of the schema. This does not provide all information of the schema, and is a work in progress - but it can be @@ -168,8 +404,3 @@ For more detailed image open link: Grade example schema diagram - -# TODOS - - * Add a remote ref test that does not require files vended from local host - * Add support for optional tests: format diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 00000000..73460ed8 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,165 @@ +analyzer: + exclude: + - test/src_list.dart + - node_modules/** + - packages/** + - test/functional/file_artifacts/** + - lib/frugal_packages/** + +# ALL lint rules are included and the ones not in use are commented out. +# An up to date list of all options is here +# http://dart-lang.github.io/linter/lints/options/options.html +# Descriptions of each rule is here http://dart-lang.github.io/linter/lints/ +# +# To ignore a lint rule on a case by case basic in code just add a comment +# above it like so: // ignore: +# example: // ignore: invalid_assignment, const_initialized_with_non_constant_value +# +# More info about config lint config options is here +# https://www.dartlang.org/guides/language/analysis-options#excluding-lines-within-a-file +linter: + rules: + # Style: Declare method return types. + # override with // ignore: always_declare_return_types + # - always_declare_return_types # render can return either ReactElement or false + + # Style: Specify type annotations. + # - always_specify_types # really annoying for local vars + + # Style: Annotate overridden members + - annotate_overrides + + # Style: Avoid using as. + # - avoid_as # comment overrides don't seem to work + + # Error: Avoid empty else statements. + - avoid_empty_else + + # Style: Don't explicitly initialize variables to null + - avoid_init_to_null + + # Style: Avoid return types on setters. + - avoid_return_types_on_setters + + # Style: Await only futures. + # - await_only_futures # TODO uncomment when you want to deal with these + + # Style: Name types using UpperCamelCase. + - camel_case_types + + # Error: Cancel instances of dart.async.StreamSubscription. + - cancel_subscriptions + + # Error: Close instances of dart.core.Sink. + - close_sinks + + # Error: Only reference in scope identifiers in doc comments. + # - comment_references # TODO uncomment when you want to deal with these + + # Style: Prefer using lowerCamelCase for constant names. + - constant_identifier_names # TODO uncomment when you want to deal with these + + # Error: Avoid control flow in finally block. + - control_flow_in_finally + + # Style: Avoid empty catch blocks. + - empty_catches + + # Style: Use ; instead of {} for empty constructor bodies. + - empty_constructor_bodies + + # Error: Avoid empty statements. + - empty_statements + + # Error: Always override hashCode if overriding == + - hash_and_equals + + # Style: Don't import implementation files from another package. + - implementation_imports + + # Error: Conditions should not unconditionally evaluate to "TRUE" or to "FALSE" + - invariant_booleans + + # Error: Invocation of Iterable.contains with references of unrelated types. + - iterable_contains_unrelated_type + + # Style: Name libraries and source files using lowercase_with_underscores. + - library_names + + # Style: Use lowercase_with_underscores when specifying a library prefix. + - library_prefixes + + # Error: Invocation of List.remove with references of unrelated types. + - list_remove_unrelated_type + + # Error: Conditions should not unconditionally evaluate to "TRUE" or to "FALSE" + - literal_only_boolean_expressions + + # Style: Name non-constant identifiers using lowerCamelCase. + # - non_constant_identifier_names # UiFactories will lint + + # Style: Avoid defining a one-member abstract class when a simple function will do. + - one_member_abstracts + + # Style: Only throw instances of classes extending either Exception or Error + - only_throw_errors + + # Style: Do not override fields. + # - overridden_fields # TODO uncomment when you want to deal with these + + # Style: Provide doc comments for all public APIs + - package_api_docs + + # Pub: Use lowercase_with_underscores for package names. + - package_names + + # Style: Prefix library names with the package name and a dot-separated path. + - package_prefixed_library_names + + # Style: Don't reassign references to parameters of functions or methods. + # - parameter_assignments # team preference + + # Style: Private field could be final. + # - prefer_final_fields # TODO uncomment when you want to deal with these + + # Style: Prefer final for variable declaration if reference is not reassigned. + - prefer_final_locals # Noisy, people use var a lot + + # Style: Use isNotEmpty for Iterables and Maps. + - prefer_is_not_empty + + # Style: Document all public members + # - public_member_api_docs # really annoying for lifecycle methods + + # Style: Prefer to use /// for doc comments + - slash_for_doc_comments + + # Style: Sort constructor declarations before method declarations. + - sort_constructors_first + + # Style: Sort unnamed constructor declarations first + - sort_unnamed_constructors_first + + # Error: Test type arguments in operator ==(Object other). + - test_types_in_equals + + # Error: Avoid throw in finally block. + - throw_in_finally + + # Style: Type annotate public APIs. + # - type_annotate_public_apis # render can return either ReactElement or false + + # Style: Don't type annotate initializing formals. + - type_init_formals + + # Style: Await for future expression statements inside async function bodies. + # - unawaited_futures # TODO uncomment when you want to deal with these + + # Style: Avoid wrapping fields in getters and setters just to be "safe". + - unnecessary_getters_setters + + # Error: Equality operator (==) invocation with references of unrelated types. + - unrelated_type_equality_checks + + # Error: Use valid regular expression syntax. + - valid_regexps diff --git a/bin/gensamples.dart b/bin/gensamples.dart index b032e1b8..1a9d2518 100644 --- a/bin/gensamples.dart +++ b/bin/gensamples.dart @@ -1,32 +1,70 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + import 'dart:io'; + import 'package:path/path.dart'; import 'package:json_schema/json_schema.dart'; +import 'package:json_schema/vm.dart'; import 'package:json_schema/schema_dot.dart'; main() { - var sourcePath = join(dirname(dirname(absolute(Platform.script.toFilePath()))), - 'dot_samples', 'schemas'); - var outPath = join(dirname(sourcePath), 'schemaout'); - new Directory(sourcePath).listSync().forEach((jsonFile) { - var fname = jsonFile.path; - var base = basenameWithoutExtension(fname); - var dotFilename = join(outPath, '$base.dot'); - var pngOut = join(outPath, '$base.png'); + configureJsonSchemaForVm(); - Schema.createSchemaFromUrl(fname) - .then((schema) { - new File(dotFilename).writeAsStringSync(createDot(schema)); - }) - .then((_) { + final sourcePath = join(dirname(dirname(absolute(Platform.script.toFilePath()))), 'dot_samples', 'schemas'); + final outPath = join(dirname(sourcePath), 'schemaout'); + Directory(sourcePath).listSync().forEach((jsonFile) { + final fname = jsonFile.path; + final base = basenameWithoutExtension(fname); + final dotFilename = join(outPath, '$base.dot'); + final pngOut = join(outPath, '$base.png'); - Process.run('dot', ['-Tpng', '-o$pngOut', dotFilename]) - .then((ProcessResult processResult) { - if(processResult.exitCode == 0) { - print("Finished running dot -Tpng -o$pngOut $fname"); - } else { - print("FAILED: running dot -Tpng -o$pngOut $fname"); - } - }); + JsonSchema.createSchemaFromUrl(fname).then((schema) { + schema.refMap.forEach((key, ref) => print('$key : $ref')); + File(dotFilename).writeAsStringSync(createDot(schema)); + }).then((_) { + Process.run('dot', ['-Tpng', '-o$pngOut', dotFilename]).then((ProcessResult processResult) { + if (processResult.exitCode == 0) { + print('Finished running dot -Tpng -o$pngOut $fname'); + } else { + print('FAILED: running dot -Tpng -o$pngOut $fname'); + } }); + }); }); -} \ No newline at end of file +} diff --git a/bin/schemadot.dart b/bin/schemadot.dart index bc099288..41f76259 100644 --- a/bin/schemadot.dart +++ b/bin/schemadot.dart @@ -1,4 +1,41 @@ #!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. /// /// Usage: schemadot --in-uri INPUT_JSON_URI --out-file OUTPUT_FILE @@ -9,16 +46,14 @@ /// the file, otherwise written to stdout. /// import 'dart:async'; -import 'dart:convert' as convert; +import 'dart:convert'; import 'dart:io'; -import 'dart:math'; + import 'package:args/args.dart'; import 'package:json_schema/json_schema.dart'; import 'package:json_schema/schema_dot.dart'; import 'package:logging/logging.dart'; -// custom -// end //! The parser for this script ArgParser _parser; //! The comment and usage associated with this script @@ -33,17 +68,16 @@ program. If [out-file] provided, output is written to the file, otherwise written to stdout. '''); - print(_parser.getUsage()); + print(_parser.usage); } //! Method to parse command line options. //! The result is a map containing all options, including positional options Map _parseArgs(List args) { ArgResults argResults; - Map result = {}; - List remaining = []; + final Map result = {}; - _parser = new ArgParser(); + _parser = ArgParser(); try { /// Fill in expectations of the parser _parser.addFlag('help', @@ -53,18 +87,8 @@ Display this help screen abbr: 'h', defaultsTo: false); - _parser.addOption('in-uri', - help: '', - defaultsTo: null, - allowMultiple: false, - abbr: 'i', - allowed: null); - _parser.addOption('out-file', - help: '', - defaultsTo: null, - allowMultiple: false, - abbr: 'o', - allowed: null); + _parser.addOption('in-uri', help: '', defaultsTo: null, abbr: 'i', allowed: null); + _parser.addOption('out-file', help: '', defaultsTo: null, abbr: 'o', allowed: null); _parser.addOption('log-level', help: r''' Select log level from: @@ -73,7 +97,6 @@ Select log level from: ''', defaultsTo: null, - allowMultiple: false, abbr: null, allowed: null); @@ -89,7 +112,7 @@ Select log level from: result['log-level'] = argResults['log-level']; if (result['log-level'] != null) { - const choices = const { + const choices = { 'all': Level.ALL, 'config': Level.CONFIG, 'fine': Level.FINE, @@ -113,59 +136,49 @@ Select log level from: } } -final _logger = new Logger('schemadot'); +final _logger = Logger('schemadot'); main(List args) { - Logger.root.onRecord.listen( - (LogRecord r) => print("${r.loggerName} [${r.level}]:\t${r.message}")); + Logger.root.onRecord.listen((LogRecord r) => print('${r.loggerName} [${r.level}]:\t${r.message}')); Logger.root.level = Level.OFF; - Map argResults = _parseArgs(args); - Map options = argResults['options']; - List positionals = argResults['rest']; + final Map argResults = _parseArgs(args); + final Map options = argResults['options']; + try { - if (options["in-uri"] == null) - throw new ArgumentError("option: in-uri is required"); + if (options['in-uri'] == null) throw ArgumentError('option: in-uri is required'); } on ArgumentError catch (e) { print(e); _usage(); exit(-1); } - // custom Logger.root.level = Level.OFF; - Completer completer = new Completer(); - Uri uri = Uri.parse(options['in-uri']); + final Completer completer = Completer(); + final Uri uri = Uri.parse(options['in-uri']); if (uri.scheme == 'http') { - new HttpClient() + HttpClient() .getUrl(uri) .then((HttpClientRequest request) => request.close()) - .then((HttpClientResponse response) => - response.transform(new convert.Utf8Decoder()).join()) + .then((HttpClientResponse response) => Utf8Decoder().bind(response).join()) .then((text) { completer.complete(text); }); } else { - File target = new File(uri.toString()); + final File target = File(uri.toString()); if (target.existsSync()) { completer.complete(target.readAsStringSync()); } } completer.future.then((schemaText) { - Future schema = Schema.createSchema(convert.JSON.decode(schemaText)); + final Future schema = JsonSchema.createSchemaAsync(json.decode(schemaText)); schema.then((schema) { - String dot = createDot(schema); + final String dot = createDot(schema); if (options['out-file'] != null) { - new File(options['out-file']).writeAsStringSync(dot); + File(options['out-file']).writeAsStringSync(dot); } else { print(dot); } }); }); - - // end } - -// custom - -// end diff --git a/codegen/json_schema.ebisu_dart.dart b/codegen/json_schema.ebisu_dart.dart deleted file mode 100644 index 068ee0bb..00000000 --- a/codegen/json_schema.ebisu_dart.dart +++ /dev/null @@ -1,354 +0,0 @@ -import "dart:io"; -import "package:path/path.dart" as path; -import "package:ebisu/ebisu_dart_meta.dart"; -import "package:ebisu/ebisu.dart"; -import "package:logging/logging.dart"; - -String _topDir; - -void main() { - Logger.root.onRecord.listen((LogRecord r) => - print("${r.loggerName} [${r.level}]:\t${r.message}")); - String here = path.absolute(Platform.script.toFilePath()); - _topDir = path.dirname(path.dirname(here)); - useDartFormatter = true; - System ebisu = system('json_schema') - ..pubSpec.homepage = 'https://github.com/patefacio/json_schema' - ..pubSpec.version = '1.0.7' - ..pubSpec.doc = 'Provide support for validating instances against json schema' - ..rootPath = '$_topDir' - ..doc = 'Json Schema related functionality' - ..testLibraries = [ - library('test_invalid_schemas') - ..includesLogger = true - ..imports = [ - 'io', '"dart:convert" as convert', '"package:path/path.dart" as path', - 'package:json_schema/json_schema.dart', - ], - library('test_validation') - ..includesLogger = true - ..imports = [ - 'io', '"dart:convert" as convert', '"package:path/path.dart" as path', - 'package:json_schema/json_schema.dart', - ], - ] - ..scripts = [ - script('schemadot') - ..imports = [ - 'package:json_schema/json_schema.dart', - 'package:json_schema/schema_dot.dart', - '"dart:convert" as convert', - 'math', - 'async', - ] - ..doc = ''' - -Usage: schemadot --in-uri INPUT_JSON_URI --out-file OUTPUT_FILE - -Given an input uri [in-uri] processes content of uri as -json schema and generates input file for Graphviz dot -program. If [out-file] provided, output is written to -the file, otherwise written to stdout. -''' - ..args = [ - scriptArg('in_uri') - ..isRequired = true - ..abbr = 'i', - scriptArg('out_file') - ..abbr = 'o', - ] - ] - ..libraries = [ - library('schema_dot') - ..doc = 'Functionality to create Graphviz input dot file from schema' - ..imports = [ - 'package:json_schema/json_schema.dart', - 'convert', - 'async', - ] - ..classes = [ - class_('schema_node') - ..doc = 'Represents one node in the schema diagram' - ..members = [ - member('schema') - ..doc = 'Referenced schema this node portrays' - ..type = 'Schema', - member('links') - ..doc = 'List of links (resulting in graph edge) from this node to another' - ..type = 'List' - ] - ], - library('json_schema') - ..doc = 'Support for validating json instances against a json schema' - ..includesLogger = true - ..enums = [ - enum_('schema_type') - ..isSnakeString = true - ..hasCustom = true - ..values = [ - id('array'), id('boolean'), id('integer'), - id('number'), id('null'), id('object'), - id('string') - ] - ] - ..imports = [ - 'io', - 'math', - '"dart:convert" as convert', - '"package:path/path.dart" as PATH', - 'async', - ] - ..parts = [ - part('schema') - ..classes = [ - class_('schema') - ..defaultMemberAccess = RO - ..ctorCustoms = [ '_fromRootMap', '_fromMap' ] - ..doc = ''' -Constructed with a json schema, either as string or Map. Validation of -the schema itself is done on construction. Any errors in the schema -result in a FormatException being thrown. -''' - ..members = [ - member('root') - ..type = 'Schema' - ..ctors = [ '_fromMap' ], - member('schema_map') - ..type = 'Map' - ..classInit = '{}' - ..ctors = [ '_fromRootMap', '_fromMap' ], - member('path') - ..ctorInit = "'#'" - ..ctors = [ '_fromMap' ], - - member('multiple_of') - ..type = 'num', - member('maximum') - ..type = 'num', - member('exclusive_maximum') - ..type = 'bool' - ..access = IA, - member('minimum') - ..type = 'num', - member('exclusive_minimum') - ..type = 'bool' - ..access = IA, - member('max_length') - ..type = 'int', - member('min_length') - ..type = 'int', - member('pattern') - ..type = 'RegExp', - - // Validation keywords for any instance - member('enum_values') - ..type = 'List' - ..classInit = '[]', - member('all_of') - ..type = 'List' - ..classInit = '[]', - member('any_of') - ..type = 'List' - ..classInit = '[]', - member('one_of') - ..type = 'List' - ..classInit = '[]', - member('not_schema') - ..type = 'Schema', - member('definitions') - ..type = 'Map' - ..classInit = '{}', - - // Meta-data - member('id') - ..type = 'Uri', - member('ref'), - member('description'), - member('title'), - - member('schema_type_list') - ..type = 'List', - member('items') - ..doc = 'To match all items to a schema' - ..type = 'Schema', - member('items_list') - ..doc = 'To match each item in array to a schema' - ..type = 'List', - member('additional_items') - ..type = 'dynamic', - member('max_items') - ..type = 'int', - member('min_items') - ..type = 'int', - member('unique_items') - ..type = 'bool' - ..classInit = 'false', - - member('required_properties') - ..type = 'List', - member('max_properties') - ..type = 'int', - member('min_properties') - ..type = 'int' - ..classInit = '0', - member('properties') - ..type = 'Map' - ..classInit = '{}', - member('additional_properties') - ..type = 'bool', - member('additional_properties_schema') - ..type = 'Schema', - member('pattern_properties') - ..type = 'Map' - ..classInit = '{}', - - member('schema_dependencies') - ..type = 'Map' - ..classInit = '{}', - member('property_dependencies') - ..type = 'Map>' - ..classInit = '{}', - - member('default_value') - ..type = 'dynamic', - - member('ref_map') - ..doc = 'Map of path to schema object' - ..type = 'Map' - ..classInit = '{}', - member('schema_refs') - ..doc = 'For schemas with \$ref maps path of schema to \$ref path' - ..type = 'Map' - ..access = IA - ..classInit = '{}', - member('schema_assignments') - ..doc = 'Assignments to call for resolution upon end of parse' - ..type = 'List' - ..classInit = '[]' - ..access = IA, - member('free_form_map') - ..doc = 'Maps any non-key top level property to its original value' - ..type = 'Map' - ..classInit = '{}' - ..access = IA, - member('this_completer') - ..type = 'Completer' - ..classInit = 'new Completer()' - ..access = IA, - member('retrieval_requests') - ..type = 'List>' - ..init = [] - ..access = IA, - member('paths_encountered') - ..doc = 'Set of strings to gaurd against path cycles' - ..type = 'Set' - ..classInit = 'new Set()' - ..access = IA, - member('format') - ..doc = 'Support for optional formats (date-time, uri, email, ipv6, hostname)' - ..access = IA, - ] - ], - part('validator') - ..variables = [ - variable('email_re') - ..isPublic = false - ..type = 'RegExp' - ..init = "new RegExp(\n" - r''' - r'^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*' - r'@' - r'[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$' -''' - ")", - variable('default_email_validator') - ..type = 'var' - ..isPublic = false - ..init = '(String email) => _emailRe.firstMatch(email) != null', - variable('email_validator') - ..isPublic = false - ..type = 'var' - ..init = '_defaultEmailValidator', - variable('ipv4_re') - ..isPublic = false - ..type = 'RegExp' - ..init = "new RegExp(\n" - r''' - r'^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$' -''' - ")", - variable('ipv6_re') - ..isPublic = false - ..type = 'RegExp' - ..init = "new RegExp(\n" - r''' - r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6}$)|' - r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}$)|' - r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}$)|' - r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}$)|' - r'(^([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}$)|' - r'(^([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1}$)|' - r'(^(([0-9a-f]{1,4}:){1,7}|:):$)|' - r'(^:(:[0-9a-f]{1,4}){1,7}$)|' - r'(^((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' - r'(^(([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' - r'(^([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^(([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)' -''' - ")", - variable('default_uri_validator') - ..type = 'var' - ..isPublic = false - ..init = ''' -(String uri) { - try { - final result = Uri.parse(uri); - if(result.path.startsWith('//')) return false; - return true; - } catch(e) { - return false; - } -}''', - variable('hostname_re') - ..isPublic = false - ..type = 'RegExp' - ..init = "new RegExp(\n" - r''' - r'^(?=.{1,255}$)' - r'[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?' - r'(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$' -''' - ")", - variable('uri_validator') - ..isPublic = false - ..type = 'var' - ..init = '_defaultUriValidator', - ] - ..classes = [ - class_('validator') - ..defaultMemberAccess = IA - ..doc = 'Initialized with schema, validates instances against it' - ..members = [ - member('root_schema') - ..type = 'Schema' - ..ctors = [''], - member('errors') - ..type = 'List' - ..access = RO - ..classInit = '[]', - member('report_multiple_errors') - ..type = 'bool', - ], - ] - ] - ]; - ebisu.generate(); -} diff --git a/dart_test.yaml b/dart_test.yaml new file mode 100644 index 00000000..02bda3dc --- /dev/null +++ b/dart_test.yaml @@ -0,0 +1,13 @@ +platforms: + - vm + - chrome +paths: + - test/unit + +concurrency: 4 +timeout: 10s +reporter: expanded + +presets: + travis: + reporter: expanded \ No newline at end of file diff --git a/dart_version.dart b/dart_version.dart new file mode 100644 index 00000000..3f244371 --- /dev/null +++ b/dart_version.dart @@ -0,0 +1,5 @@ +import 'dart:io'; + +main() { + print(Platform.version.split('.').first); +} diff --git a/example/from_json/movie_sample.dart b/example/from_json/movie_sample.dart index d2eac747..c49c8f6b 100644 --- a/example/from_json/movie_sample.dart +++ b/example/from_json/movie_sample.dart @@ -1,7 +1,44 @@ #!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. -import "package:json_schema/json_schema.dart"; -import "package:logging/logging.dart"; +import 'package:json_schema/json_schema.dart'; +import 'package:logging/logging.dart'; main() { Logger.root.onRecord.listen((LogRecord rec) { @@ -12,55 +49,45 @@ main() { ////////////////////////////////////////////////////////////////////// // Define schema in code ////////////////////////////////////////////////////////////////////// - var movieSchema = { - "title" : "movie data", - "additionalProperties" : false, - "required" : [ "movies" ], - "properties" : { - "movies" : { r"$ref" : "#/definitions/movie_map" } + final movieSchema = { + 'title': 'movie data', + 'additionalProperties': false, + 'required': ['movies'], + 'properties': { + 'movies': {r'$ref': '#/definitions/movie_map'} }, - "definitions" : { - "movie" : { - "additionalProperties": false, - "required" : [ "title", "year_made", "rating" ], - "properties": { - "title" : { "type" : "string" }, - "year_made" : { "type" : "integer" }, - "rating" : { "type" : "integer" } + 'definitions': { + 'movie': { + 'additionalProperties': false, + 'required': ['title', 'year_made', 'rating'], + 'properties': { + 'title': {'type': 'string'}, + 'year_made': {'type': 'integer'}, + 'rating': {'type': 'integer'} } }, - "movie_map" : { - "type": "object", - "additionalProperties": { r"$ref": "#/definitions/movie" }, - "default": {} + 'movie_map': { + 'type': 'object', + 'additionalProperties': {r'$ref': '#/definitions/movie'}, + 'default': {} } } }; - var movies = { - "movies" : { - "the mission" : { - "title":"The Mission", - "year_made":1986, - "rating":5 - }, - "troll 2" : { - "title":"Troll 2", - "year_made":1990, - "rating":2 - } + final movies = { + 'movies': { + 'the mission': {'title': 'The Mission', 'year_made': 1986, 'rating': 5}, + 'troll 2': {'title': 'Troll 2', 'year_made': 1990, 'rating': 2} } }; - Schema.createSchema(movieSchema) - .then((schema) { - var validator = new Validator(schema); - bool validates = validator.validate(movies); - if(!validates) { - print("Errors: ${validator.errors}"); - } else { - print('$movies:\nvalidates!'); - } - }); - + JsonSchema.createSchemaAsync(movieSchema).then((schema) { + final validator = Validator(schema); + final bool validates = validator.validate(movies); + if (!validates) { + print('Errors: ${validator.errors}'); + } else { + print('$movies:\nvalidates!'); + } + }); } diff --git a/example/from_json/validate_json_from_data.dart b/example/from_json/validate_json_from_data.dart index 6640c080..628a626b 100644 --- a/example/from_json/validate_json_from_data.dart +++ b/example/from_json/validate_json_from_data.dart @@ -1,30 +1,61 @@ #!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. -import "package:json_schema/json_schema.dart"; -import "package:logging/logging.dart"; +import 'package:json_schema/json_schema.dart'; +import 'package:logging/logging.dart'; main() { - - Logger.root.onRecord.listen((LogRecord rec) => - print('${rec.level.name}: ${rec.time}: ${rec.message}')); + Logger.root.onRecord.listen((LogRecord rec) => print('${rec.level.name}: ${rec.time}: ${rec.message}')); Logger.root.level = Level.SHOUT; ////////////////////////////////////////////////////////////////////// // Define schema in code ////////////////////////////////////////////////////////////////////// - var mustBeIntegerSchema = { - "type" : "integer" - }; - - var n = 3; - var decimals = 3.14; - var str = 'hi'; + final mustBeIntegerSchema = {'type': 'integer'}; - Schema.createSchema(mustBeIntegerSchema) - .then((schema) { - print('$n => ${schema.validate(n)}'); - print('$decimals => ${schema.validate(decimals)}'); - print('$str => ${schema.validate(str)}'); - }); + final n = 3; + final decimals = 3.14; + final str = 'hi'; + JsonSchema.createSchemaAsync(mustBeIntegerSchema).then((schema) { + print('$n => ${schema.validate(n)}'); + print('$decimals => ${schema.validate(decimals)}'); + print('$str => ${schema.validate(str)}'); + }); } diff --git a/example/from_url/validate_instance_from_url.dart b/example/from_url/validate_instance_from_url.dart index a9a29345..f76e9a77 100644 --- a/example/from_url/validate_instance_from_url.dart +++ b/example/from_url/validate_instance_from_url.dart @@ -1,70 +1,96 @@ #!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. -import "dart:convert" as convert; -import "package:json_schema/json_schema.dart"; -import "package:logging/logging.dart"; +import 'dart:convert'; -main() { +import 'package:json_schema/json_schema.dart'; +import 'package:logging/logging.dart'; - Logger.root.onRecord.listen((LogRecord rec) => - print('${rec.level.name}: ${rec.time}: ${rec.message}')); +main() { + Logger.root.onRecord.listen((LogRecord rec) => print('${rec.level.name}: ${rec.time}: ${rec.message}')); Logger.root.level = Level.SHOUT; ////////////////////////////////////////////////////////////////////// // Pull in schema from web ////////////////////////////////////////////////////////////////////// - String url = "http://json-schema.org/draft-04/schema"; - Schema.createSchemaFromUrl(url) - .then((Schema schema) { - - // TODO: Figure out the redirect issues here - if(false) { - print('''Does schema validate itself? - ${schema.validate(schema.schemaMap)}'''); - } - - var validSchema = { "type" : "integer" }; - print('''Does schema validate valid schema $validSchema? + String url = 'http://json-schema.org/draft-04/schema'; + JsonSchema.createSchemaFromUrl(url).then((JsonSchema schema) { + final validSchema = {'type': 'integer'}; + print('''Does schema validate valid schema $validSchema? ${schema.validate(validSchema)}'''); - var invalidSchema = { "type" : "nibble" }; - print('''Does schema validate invalid schema $invalidSchema? + final invalidSchema = {'type': 'nibble'}; + print('''Does schema validate invalid schema $invalidSchema? ${schema.validate(invalidSchema)}'''); - - }); + }); ////////////////////////////////////////////////////////////////////// // Pull in schema from file in current directory ////////////////////////////////////////////////////////////////////// - url = "grades_schema.json"; - Schema.createSchemaFromUrl(url) - .then((schema) { - var grades = convert.JSON.decode(''' + url = 'grades_schema.json'; + JsonSchema.createSchemaFromUrl(url).then((schema) { + final grades = json.decode(''' { - "semesters": [ + 'semesters': [ { - "semester": 1, - "grades": [ + 'semester': 1, + 'grades': [ { - "type": "homework", - "date": "09/27/2013", - "grade": 100, - "avg": 93, - "std": 8 + 'type': 'homework', + 'date': '09/27/2013', + 'grade': 100, + 'avg': 93, + 'std': 8 }, { - "type": "homework", - "date": "09/28/2013", - "grade": 100, - "avg": 60, - "std": 25 + 'type': 'homework', + 'date': '09/28/2013', + 'grade': 100, + 'avg': 60, + 'std': 25 } ] } ] }'''); - print('''Does grades schema validate $grades + print('''Does grades schema validate $grades ${schema.validate(grades)}'''); - }); + }); } diff --git a/example/readme/asynchronous_creation/from_file.dart b/example/readme/asynchronous_creation/from_file.dart new file mode 100644 index 00000000..67305ec6 --- /dev/null +++ b/example/readme/asynchronous_creation/from_file.dart @@ -0,0 +1,77 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final file = "example/readme/asynchronous_creation/geo.schema.json"; + + final schema = await JsonSchema.createSchemaFromUrl(file); + + // Create some examples to validate against the schema. + final workivaAmes = { + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }; + + final nowhereville = { + 'latitude': -2000, + 'longitude': 7836, + }; + + print('$workivaAmes => ${schema.validate(workivaAmes)}'); // true + print('$nowhereville => ${schema.validate(nowhereville)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} diff --git a/example/readme/asynchronous_creation/from_url.dart b/example/readme/asynchronous_creation/from_url.dart new file mode 100644 index 00000000..b55ba2e1 --- /dev/null +++ b/example/readme/asynchronous_creation/from_url.dart @@ -0,0 +1,72 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final url = "https://raw.githubusercontent.com/json-schema-org/JSON-Schema-Test-Suite/master/remotes/integer.json"; + + final schema = await JsonSchema.createSchemaFromUrl(url); + + // Create some examples to validate against the schema. + final n = 3; + final decimals = 3.14; + final str = 'hi'; + + print('$n => ${schema.validate(n)}'); // true + print('$decimals => ${schema.validate(decimals)}'); // false + print('$str => ${schema.validate(str)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} diff --git a/example/readme/asynchronous_creation/geo.schema.json b/example/readme/asynchronous_creation/geo.schema.json new file mode 100644 index 00000000..4fa0c1cc --- /dev/null +++ b/example/readme/asynchronous_creation/geo.schema.json @@ -0,0 +1,20 @@ +{ + "id": "https://example.com/geographical-location.schema.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Longitude and Latitude Values", + "description": "A geographical coordinate.", + "required": [ "latitude", "longitude" ], + "type": "object", + "properties": { + "latitude": { + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "longitude": { + "type": "number", + "minimum": -180, + "maximum": 180 + } + } + } \ No newline at end of file diff --git a/example/readme/asynchronous_creation/remote_http_refs.dart b/example/readme/asynchronous_creation/remote_http_refs.dart new file mode 100644 index 00000000..b63db581 --- /dev/null +++ b/example/readme/asynchronous_creation/remote_http_refs.dart @@ -0,0 +1,78 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + // Schema Defined as a JSON String + final schema = await JsonSchema.createSchemaAsync(r''' + { + "type": "array", + "items": { + "$ref": "https://raw.githubusercontent.com/json-schema-org/JSON-Schema-Test-Suite/master/remotes/integer.json" + } + } + '''); + + // Create some examples to validate against the schema. + final numbersArray = [1, 2, 3]; + final decimalsArray = [3.14, 1.2, 5.8]; + final strArray = ['hello', 'world']; + + print('$numbersArray => ${schema.validate(numbersArray)}'); // true + print('$decimalsArray => ${schema.validate(decimalsArray)}'); // false + print('$strArray => ${schema.validate(strArray)}'); // false + + // Exit the process cleanly (VM Only). + exit(0); +} diff --git a/example/readme/asynchronous_creation/remote_ref_cache.dart b/example/readme/asynchronous_creation/remote_ref_cache.dart new file mode 100644 index 00000000..dacf2e71 --- /dev/null +++ b/example/readme/asynchronous_creation/remote_ref_cache.dart @@ -0,0 +1,125 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:json_schema/json_schema.dart'; + +// For VM: +import 'package:json_schema/vm.dart'; + +// For Browser: +// import 'package:json_schema/browser.dart'; + +main() async { + // For VM: + configureJsonSchemaForVm(); + + // For Browser: + // configureJsonSchemaForBrowser(); + + final referencedSchema = { + r"$id": "https://example.com/geographical-location.schema.json", + r"$schema": "http://json-schema.org/draft-06/schema#", + "title": "Longitude and Latitude", + "description": "A geographical coordinate on a planet (most commonly Earth).", + "required": ["latitude", "longitude"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "latitude": {"type": "number", "minimum": -90, "maximum": 90}, + "longitude": {"type": "number", "minimum": -180, "maximum": 180} + } + }; + + final RefProvider refProvider = RefProvider.asyncSchema((String ref) async { + final Map references = { + 'https://example.com/geographical-location.schema.json': JsonSchema.createSchema(referencedSchema), + }; + + if (references.containsKey(ref)) { + // Silly example that adds a 1 second delay. + // In practice, you could make any service call here, + // parse the results into a schema, and return. + await Future.delayed(Duration(seconds: 1)); + return references[ref]; + } + + // Fall back to default URL $ref behavior + return await JsonSchema.createSchemaFromUrl(ref); + }); + + final schema = await JsonSchema.createSchemaAsync({ + 'type': 'array', + 'items': {r'$ref': 'https://example.com/geographical-location.schema.json'} + }, refProvider: refProvider); + + final workivaLocations = [ + { + 'name': 'Ames', + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }, + { + 'name': 'Scottsdale', + 'latitude': 33.4634707, + 'longitude': -111.9266617, + } + ]; + + final badLocations = [ + { + 'name': 'Bad Badlands', + 'latitude': 181, + 'longitude': 92, + }, + { + 'name': 'Nowhereville', + 'latitude': -2000, + 'longitude': 7836, + } + ]; + + print('${json.encode(workivaLocations)} => ${schema.validate(workivaLocations)}'); + print('${json.encode(badLocations)} => ${schema.validate(badLocations)}'); + + exit(0); +} diff --git a/example/readme/synchronous_creation/local_ref_cache.dart b/example/readme/synchronous_creation/local_ref_cache.dart new file mode 100644 index 00000000..75004331 --- /dev/null +++ b/example/readme/synchronous_creation/local_ref_cache.dart @@ -0,0 +1,104 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:convert'; + +import 'package:json_schema/json_schema.dart'; + +main() { + final referencedSchema = { + r"$id": "https://example.com/geographical-location.schema.json", + r"$schema": "http://json-schema.org/draft-06/schema#", + "title": "Longitude and Latitude", + "description": "A geographical coordinate on a planet (most commonly Earth).", + "required": ["latitude", "longitude"], + "type": "object", + "properties": { + "name": {"type": "string"}, + "latitude": {"type": "number", "minimum": -90, "maximum": 90}, + "longitude": {"type": "number", "minimum": -180, "maximum": 180} + } + }; + + final RefProvider refProvider = RefProvider.syncSchema((String ref) { + final Map references = { + 'https://example.com/geographical-location.schema.json': JsonSchema.createSchema(referencedSchema), + }; + + if (references.containsKey(ref)) { + return references[ref]; + } + + return null; + }); + + final schema = JsonSchema.createSchema({ + 'type': 'array', + 'items': {r'$ref': 'https://example.com/geographical-location.schema.json'} + }, refProvider: refProvider); + + final workivaLocations = [ + { + 'name': 'Ames', + 'latitude': 41.9956731, + 'longitude': -93.6403663, + }, + { + 'name': 'Scottsdale', + 'latitude': 33.4634707, + 'longitude': -111.9266617, + } + ]; + + final badLocations = [ + { + 'name': 'Bad Badlands', + 'latitude': 181, + 'longitude': 92, + }, + { + 'name': 'Nowhereville', + 'latitude': -2000, + 'longitude': 7836, + } + ]; + + print('${json.encode(workivaLocations)} => ${schema.validate(workivaLocations)}'); + print('${json.encode(badLocations)} => ${schema.validate(badLocations)}'); +} diff --git a/example/readme/synchronous_creation/self_contained.dart b/example/readme/synchronous_creation/self_contained.dart new file mode 100644 index 00000000..a97034c3 --- /dev/null +++ b/example/readme/synchronous_creation/self_contained.dart @@ -0,0 +1,57 @@ +#!/usr/bin/env dart +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'package:json_schema/json_schema.dart'; + +main() { + /// Define schema in a Dart [Map] or use a JSON [String]. + final mustBeIntegerSchemaMap = {"type": "integer"}; + + // Create some examples to validate against the schema. + final n = 3; + final decimals = 3.14; + final str = 'hi'; + + // Construct the schema from the schema map or JSON string. + final schema = JsonSchema.createSchema(mustBeIntegerSchemaMap); + + print('$n => ${schema.validate(n)}'); // true + print('$decimals => ${schema.validate(decimals)}'); // false + print('$str => ${schema.validate(str)}'); // false +} diff --git a/lib/browser.dart b/lib/browser.dart new file mode 100644 index 00000000..e6c742ec --- /dev/null +++ b/lib/browser.dart @@ -0,0 +1,52 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +library json_schema.browser; + +import 'package:w_transport/browser.dart'; + +import 'package:json_schema/src/json_schema/global_platform_functions.dart'; +import 'package:json_schema/src/json_schema/browser/platform_functions.dart' show createSchemaFromUrlBrowser; + +export 'package:json_schema/src/json_schema/browser/platform_functions.dart' show createSchemaFromUrlBrowser; + +/// Configures json_schema for use in the browser via dart:html. +void configureJsonSchemaForBrowser() { + configureWTransportForBrowser(); + globalCreateJsonSchemaFromUrl = createSchemaFromUrlBrowser; +} diff --git a/lib/json_schema.dart b/lib/json_schema.dart index a5fcd994..11015b37 100644 --- a/lib/json_schema.dart +++ b/lib/json_schema.dart @@ -1,127 +1,43 @@ -/// Support for validating json instances against a json schema -library json_schema.json_schema; - -import 'dart:async'; -import 'dart:convert' as convert; -import 'dart:io'; -import 'dart:math'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as PATH; - -// custom -// end - -part 'src/json_schema/schema.dart'; -part 'src/json_schema/validator.dart'; - -final Logger _logger = new Logger('json_schema'); - -class SchemaType implements Comparable { - static const SchemaType ARRAY = const SchemaType._(0); - - static const SchemaType BOOLEAN = const SchemaType._(1); - - static const SchemaType INTEGER = const SchemaType._(2); - - static const SchemaType NUMBER = const SchemaType._(3); - - static const SchemaType NULL = const SchemaType._(4); - - static const SchemaType OBJECT = const SchemaType._(5); - - static const SchemaType STRING = const SchemaType._(6); - - static List get values => - const [ARRAY, BOOLEAN, INTEGER, NUMBER, NULL, OBJECT, STRING]; - - final int value; - - int get hashCode => value; - - const SchemaType._(this.value); - - SchemaType copy() => this; - - int compareTo(SchemaType other) => value.compareTo(other.value); - - String toString() { - switch (this) { - case ARRAY: - return "array"; - case BOOLEAN: - return "boolean"; - case INTEGER: - return "integer"; - case NUMBER: - return "number"; - case NULL: - return "null"; - case OBJECT: - return "object"; - case STRING: - return "string"; - } - return null; - } - - static SchemaType fromString(String s) { - if (s == null) return null; - switch (s) { - case "array": - return ARRAY; - case "boolean": - return BOOLEAN; - case "integer": - return INTEGER; - case "number": - return NUMBER; - case "null": - return NULL; - case "object": - return OBJECT; - case "string": - return STRING; - default: - return null; - } - } - - // custom - // end - -} - -// custom - -/// Used to provide your own uri validator (if default does not suit needs) -set uriValidator(bool validator(String s)) => _uriValidator = validator; - -/// Used to provide your own email validator (if default does not suit needs) -set emailValidator(bool validator(String s)) => _emailValidator = validator; - -bool logFormatExceptions = false; - -bool _jsonEqual(a, b) { - bool result = true; - if (a is Map && b is Map) { - if (a.length != b.length) return false; - a.keys.forEach((k) { - if (!_jsonEqual(a[k], b[k])) { - result = false; - return; - } - }); - } else if (a is List && b is List) { - if (a.length != b.length) return false; - for (int i = 0; i < a.length; i++) { - if (!_jsonEqual(a[i], b[i])) { - return false; - } - } - } else { - return a == b; - } - return result; -} - -// end +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +export 'package:json_schema/src/json_schema/json_schema.dart' show JsonSchema; +export 'package:json_schema/src/json_schema/constants.dart' show SchemaVersion; +export 'package:json_schema/src/json_schema/schema_type.dart' show SchemaType; +export 'package:json_schema/src/json_schema/validator.dart' show Validator, ValidationError; +export 'package:json_schema/src/json_schema/ref_provider.dart' show RefProvider; diff --git a/lib/schema_dot.dart b/lib/schema_dot.dart index 90d8c9d2..16e081cb 100644 --- a/lib/schema_dot.dart +++ b/lib/schema_dot.dart @@ -1,26 +1,61 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + /// Functionality to create Graphviz input dot file from schema library json_schema.schema_dot; import 'package:json_schema/json_schema.dart'; -// custom -// end - /// Represents one node in the schema diagram +/// +@Deprecated('This functionality is not maintained, is untested, and may be removed in a future release.') class SchemaNode { + SchemaNode(this.schema, [this.links]) { + if (links == null) links = []; + } + /// Referenced schema this node portrays - Schema schema; + JsonSchema schema; /// List of links (resulting in graph edge) from this node to another List links; - // custom - - SchemaNode(Schema this.schema, [this.links]) { - if (links == null) links = []; - } - - static bool schemaShown(Schema schema) => + static bool schemaShown(JsonSchema schema) => schema.properties.length > 0 || schema.definitions.length > 0 || schema.anyOf.length > 0 || @@ -33,45 +68,42 @@ class SchemaNode { schema.ref != null; String get nodes { - List lines = schema.refMap.values + final List lines = schema.refMap.values .where((schema) => schemaShown(schema)) - .map((schema) => new SchemaNode(schema, links).node) + .map((schema) => SchemaNode(schema, links).node) .toList(); - lines.addAll(links.map((link) => "$link;")); + lines.addAll(links.map((link) => '$link;')); return lines.join('\n'); } String get node { - var data = ['"${schema.path}" [']..add(label.join('\n'))..add(']'); + final data = ['"${schema.path}" [']..add(label.join('\n'))..add(']'); return data.join('\n'); } - static dynamic schemaType(Schema schema) { + static dynamic schemaType(JsonSchema schema) { dynamic result; - var schemaTypeList = schema.schemaTypeList; - if (schemaTypeList == null) { + final typeList = schema.typeList; + if (typeList == null) { if (schema.oneOf.length > 0) { - result = - "oneOf:${schema.oneOf.map((schema) => schemaType(schema)).toList()}"; + result = 'oneOf:${schema.oneOf.map((schema) => schemaType(schema)).toList()}'; } else if (schema.anyOf.length > 0) { - result = - "anyOf:${schema.anyOf.map((schema) => schemaType(schema)).toList()}"; + result = 'anyOf:${schema.anyOf.map((schema) => schemaType(schema)).toList()}'; } else if (schema.allOf.length > 0) { - result = - "allOf:${schema.allOf.map((schema) => schemaType(schema)).toList()}"; + result = 'allOf:${schema.allOf.map((schema) => schemaType(schema)).toList()}'; } else if (schema.defaultValue != null) { - result = "default=${schema.defaultValue}"; + result = 'default=${schema.defaultValue}'; } else if (schema.ref != null) { - result = "ref=${schema.ref}"; + result = 'ref=${schema.ref}'; } else if (schema.enumValues != null && schema.enumValues.length > 0) { - result = "enum=${schema.enumValues}"; + result = 'enum=${schema.enumValues}'; } else if (schema.schemaMap.length == 0) { - result = "{}"; + result = '{}'; } else { - result = "$schema"; + result = '$schema'; } } else { - result = schemaTypeList.length == 1 ? schemaTypeList[0] : schemaTypeList; + result = typeList.length == 1 ? typeList[0] : typeList; } if ((result is List) && result.length == 0) result = {}; if (result == null) result = {}; @@ -81,7 +113,7 @@ class SchemaNode { List get label { return ['label =<'] ..add('') - ..add(wrap(schema.path, port: "@path")) + ..add(wrap(schema.path, port: '@path')) ..add(wrap(title)) ..add(wrap(description)) ..addAll(definitionEntries) @@ -101,95 +133,92 @@ class SchemaNode { } List get defaultValue { - List result = []; + final List result = []; if (schema.defaultValue != null) { - result.add(wrapRowDistinct('default', "${schema.defaultValue}")); + result.add(wrapRowDistinct('default', '${schema.defaultValue}')); } return result; } List get minimum { - List result = []; + final List result = []; if (schema.minimum != null) { - result.add(wrapRowDistinct('minimum', "${schema.minimum}")); + result.add(wrapRowDistinct('minimum', '${schema.minimum}')); } return result; } List get maximum { - List result = []; + final List result = []; if (schema.maximum != null) { - result.add(wrapRowDistinct('maximum', "${schema.maximum}")); + result.add(wrapRowDistinct('maximum', '${schema.maximum}')); } return result; } List get multipleOf { - List result = []; + final List result = []; if (schema.multipleOf != null) { - result.add(wrapRowDistinct('multipleOf', "${schema.multipleOf}")); + result.add(wrapRowDistinct('multipleOf', '${schema.multipleOf}')); } return result; } List get enumEntries { - List enumValues = []; + final List enumValues = []; if (schema.enumValues.length > 0) { enumValues.add(wrap('Enum Values', color: 'beige')); schema.enumValues.forEach((value) { - enumValues.add(wrap("$value", color: 'grey')); + enumValues.add(wrap('$value', color: 'grey')); }); } return enumValues; } List get anyOf { - List anyOf = []; + final List anyOf = []; if (schema.anyOf.length > 0) { anyOf.add(wrap('Any Of', color: 'beige')); int i = 0; schema.anyOf.forEach((anyOfSchema) { - String port = "${i++}"; + final String port = '${i++}'; makeSchemaLink(port, schema, anyOfSchema); - anyOf.add(wrap(abbreviatedString("${schemaType(anyOfSchema)}", 30), - color: 'grey', port: port)); + anyOf.add(wrap(abbreviatedString('${schemaType(anyOfSchema)}', 30), color: 'grey', port: port)); }); } return anyOf; } List get oneOf { - List oneOf = []; + final List oneOf = []; if (schema.oneOf.length > 0) { oneOf.add(wrap('One Of', color: 'beige')); int i = 0; schema.oneOf.forEach((oneOfSchema) { - String port = "${i++}"; + final String port = '${i++}'; makeSchemaLink(port, schema, oneOfSchema); - oneOf.add(wrap(abbreviatedString("${schemaType(oneOfSchema)}", 30), - color: 'grey', port: port)); + oneOf.add(wrap(abbreviatedString('${schemaType(oneOfSchema)}', 30), color: 'grey', port: port)); }); } return oneOf; } List get allOf { - List allOf = []; + final List allOf = []; if (schema.allOf.length > 0) { allOf.add(wrap('All Of', color: 'beige')); int i = 0; schema.allOf.forEach((allOfSchema) { - String port = "${i++}"; + final String port = '${i++}'; makeSchemaLink(port, schema, allOfSchema); - allOf.add(wrap(abbreviatedString("${schemaType(allOfSchema)}", 30), - color: 'grey', port: port)); + allOf.add(wrap(abbreviatedString('${schemaType(allOfSchema)}', 30), color: 'grey', port: port)); }); } return allOf; } List get propertyDependencies { - List result = []; + final List result = []; if (schema.propertyDependencies.length > 0) { result.add(wrap('Property Dependencies')); schema.propertyDependencies.forEach((key, val) { @@ -200,7 +229,7 @@ class SchemaNode { } List get schemaDependencies { - List result = []; + final List result = []; if (schema.schemaDependencies.length > 0) { result.add('Property Dependencies'); schema.propertyDependencies.forEach((key, val) { @@ -210,78 +239,68 @@ class SchemaNode { return result; } - makeSchemaPort(String port, Schema schema) => '"${schema.path}":"$port"'; + makeSchemaPort(String port, JsonSchema schema) => '"${schema.path}":"$port"'; - makeSchemaLink(String port, Schema src, Schema target) { - if (schemaShown(target)) - links.add( - '${makeSchemaPort(port, src)} -> ${makeSchemaPort("@path", target)}'); + makeSchemaLink(String port, JsonSchema src, JsonSchema target) { + if (schemaShown(target)) links.add('${makeSchemaPort(port, src)} -> ${makeSchemaPort("@path", target)}'); } List get additionalPropertiesSchema { - List result = []; - Schema other = schema.additionalPropertiesSchema; + final List result = []; + final JsonSchema other = schema.additionalPropertiesSchema; if (other != null) { result.add(wrap('Additional Properties', color: 'lemonchiffon')); - String port = "mustBe"; + final String port = 'mustBe'; makeSchemaLink(port, schema, other); - result.add(wrapRowDistinct('Must Be: ', - abbreviatedString(schemaType(other).toString(), 30), port)); + result.add(wrapRowDistinct('Must Be: ', abbreviatedString(schemaType(other).toString(), 30), port)); } return result; } List get propertyEntries { - List props = []; + final List props = []; if (schema.properties.length > 0) { props.add(wrap('Properties')); - var sortedProps = new List.from(schema.properties.keys)..sort(); + final sortedProps = List.from(schema.properties.keys)..sort(); sortedProps.forEach((prop) { - var propertySchema = schema.properties[prop]; - String requiredPrefix = schema.propertyRequired(prop) ? '! ' : '? '; - String port = "@$prop"; + final propertySchema = schema.properties[prop]; + final String requiredPrefix = schema.propertyRequired(prop) ? '! ' : '? '; + final String port = '@$prop'; if (schemaShown(propertySchema)) { makeSchemaLink(port, schema, propertySchema); - } else if (propertySchema.items is Schema && - schemaShown(propertySchema.items)) { + } else if (propertySchema.items is JsonSchema && schemaShown(propertySchema.items)) { makeSchemaLink(port, schema, propertySchema.items); } props.add(wrapRowDistinct( - "$requiredPrefix$prop", - abbreviatedString(schemaType(propertySchema).toString(), 30), - port)); + '$requiredPrefix$prop', abbreviatedString(schemaType(propertySchema).toString(), 30), port)); }); } return props; } List get definitionEntries { - List definitions = []; + final List definitions = []; if (schema.definitions.length > 0) { - definitions.add( - ''); - var sortedDefinitions = new List.from(schema.definitions.keys)..sort(); + definitions + .add(''); + final sortedDefinitions = List.from(schema.definitions.keys)..sort(); sortedDefinitions.forEach((key) { - definitions.add(wrapRowDistinct(key, '', "${schema.path}@$key")); + definitions.add(wrapRowDistinct(key, '', '${schema.path}@$key')); }); } return definitions; } - String wrap(String s, {String port: '', String color: 'wheat'}) => s == null + String wrap(String s, {String port = '', String color = 'wheat'}) => s == null ? '' : ''; String wrapRowDistinct(String first, String second, [String port = '']) => '$first'; - String get title => schema.title != null - ? abbreviatedString("title=${schema.title}", 30) - : null; + String get title => schema.title != null ? abbreviatedString("title=${schema.title}", 30) : null; - String get description => schema.description != null - ? abbreviatedString("descr=${schema.description}", 30) - : null; + String get description => schema.description != null ? abbreviatedString('descr=${schema.description}', 30) : null; String abbreviatedString(String s, [int len = 15]) { if (s == null) return s; @@ -292,15 +311,11 @@ class SchemaNode { return s.substring(0, len) + '...'; } } - - // end - } -// custom - /// Return a dot specification for [schema] -String createDot(Schema schema) => ''' +@Deprecated('This functionality is not maintained, is untested, and may be removed in a future release.') +String createDot(JsonSchema schema) => ''' digraph G { fontname = "Bitstream Vera Sans" fontsize = 8 @@ -316,11 +331,9 @@ digraph G { fontsize = 8 ] -${new SchemaNode(schema).nodes} +${SchemaNode(schema).nodes} } '''; - -// end diff --git a/lib/src/json_schema/browser/platform_functions.dart b/lib/src/json_schema/browser/platform_functions.dart new file mode 100644 index 00000000..0e2f06df --- /dev/null +++ b/lib/src/json_schema/browser/platform_functions.dart @@ -0,0 +1,64 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:async'; + +import 'package:w_transport/w_transport.dart'; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/json_schema.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; + +Future createSchemaFromUrlBrowser(String schemaUrl, {SchemaVersion schemaVersion}) async { + final uriWithFrag = Uri.parse(schemaUrl); + var uri = uriWithFrag.removeFragment(); + if (schemaUrl.endsWith('#')) { + uri = uriWithFrag; + } + if (uri.scheme != 'file') { + // _logger.info('Getting url $uri'); TODO: re-add logger. + final response = await (JsonRequest()..uri = uri).get(); + // HTTP servers ignore fragments, so resolve a sub-map if a fragment was specified. + final parentSchema = + await JsonSchema.createSchemaAsync(response.body.asJson(), schemaVersion: schemaVersion, fetchedFromUri: uri); + final schema = JsonSchemaUtils.getSubMapFromFragment(parentSchema, uriWithFrag); + return schema ?? parentSchema; + } else { + throw FormatException('Url schema must be http: $schemaUrl. To use a local file, use dart:io'); + } +} diff --git a/lib/src/json_schema/constants.dart b/lib/src/json_schema/constants.dart new file mode 100644 index 00000000..bd7ffd78 --- /dev/null +++ b/lib/src/json_schema/constants.dart @@ -0,0 +1,439 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +class JsonSchemaValidationRegexes { + static RegExp email = RegExp(r'^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*' + r'@' + r'[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$'); + + static RegExp ipv4 = RegExp(r'^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' + r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' + r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' + r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$'); + + static RegExp ipv6 = RegExp(r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6}$)|' + r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}$)|' + r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}$)|' + r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}$)|' + r'(^([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}$)|' + r'(^([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1}$)|' + r'(^(([0-9a-f]{1,4}:){1,7}|:):$)|' + r'(^:(:[0-9a-f]{1,4}){1,7}$)|' + r'(^((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' + r'(^(([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' + r'(^([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^(([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' + r'(^:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)'); + + static RegExp hostname = RegExp(r'^(?=.{1,255}$)' + r'[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?' + r'(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$'); + + static RegExp jsonPointer = RegExp(r'^(?:\/(?:[^~/]|~0|~1)*)*$'); +} + +class SchemaVersion implements Comparable { + const SchemaVersion._(this.value); + + static const SchemaVersion draft4 = SchemaVersion._(0); + + static const SchemaVersion draft6 = SchemaVersion._(1); + + static List get values => const [draft4, draft6]; + + final int value; + + @override + int get hashCode => value; + + SchemaVersion copy() => this; + + @override + int compareTo(SchemaVersion other) => value.compareTo(other.value); + + @override + String toString() { + switch (this) { + case draft4: + return 'http://json-schema.org/draft-04/schema#'; + case draft6: + return 'http://json-schema.org/draft-06/schema#'; + } + return null; + } + + static SchemaVersion fromString(String s) { + if (s == null) return null; + switch (s) { + case 'http://json-schema.org/draft-04/schema#': + return draft4; + case 'http://json-schema.org/draft-06/schema#': + return draft6; + default: + return null; + } + } +} + +String getJsonSchemaDefinitionByRef(String ref) { + final mapping = { + SchemaVersion.draft4.toString(): JsonSchemaDefinitions.draft4, + SchemaVersion.draft6.toString(): JsonSchemaDefinitions.draft6, + }; + + if (SchemaVersion.values.map((value) => value.toString()).contains(ref)) { + return mapping[ref]; + } + + return null; +} + +class JsonSchemaDefinitions { + static String draft4 = r''' + { + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "positiveInteger": { + "type": "integer", + "minimum": 0 + }, + "positiveIntegerDefault0": { + "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] + }, + "simpleTypes": { + "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "uniqueItems": true + } + }, + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "$schema": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { "$ref": "#/definitions/positiveInteger" }, + "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/positiveInteger" }, + "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { "$ref": "#/definitions/positiveInteger" }, + "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "dependencies": { + "exclusiveMaximum": [ "maximum" ], + "exclusiveMinimum": [ "minimum" ] + }, + "default": {} +} + '''; + + static String draft6 = r''' + { + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "http://json-schema.org/draft-06/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} + '''; +} diff --git a/lib/src/json_schema/format_exceptions.dart b/lib/src/json_schema/format_exceptions.dart new file mode 100644 index 00000000..c006e98e --- /dev/null +++ b/lib/src/json_schema/format_exceptions.dart @@ -0,0 +1,22 @@ +class FormatExceptions { + static FormatException error(String msg, [String path]) { + msg = '${path ?? ''}: $msg'; + // if (logFormatExceptions) _logger.warning(msg); TODO: re-add logger + return FormatException(msg); + } + + static FormatException bool(String key, dynamic instance, [String path]) => + error('$key must be boolean: $instance', path); + static FormatException num(String key, dynamic instance, [String path]) => error('$key must be num: $instance', path); + static FormatException nonNegativeNum(String key, dynamic instance, [String path]) => + error('multipleOf must be > 0: $instance'); + static FormatException int(String key, dynamic instance, [String path]) => error('$key must be int: $instance', path); + static FormatException string(String key, dynamic instance, [String path]) => + error('$key must be string: $instance', path); + static FormatException object(String key, dynamic instance, [String path]) => + error('$key must be object: $instance', path); + static FormatException list(String key, dynamic instance, [String path]) => + error('$key must be array: $instance', path); + static FormatException schema(String key, dynamic instance, [String path]) => + error('$key must be valid schema object or boolean: $instance', path); +} diff --git a/lib/src/json_schema/global_platform_functions.dart b/lib/src/json_schema/global_platform_functions.dart new file mode 100644 index 00000000..a53541cf --- /dev/null +++ b/lib/src/json_schema/global_platform_functions.dart @@ -0,0 +1,74 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'package:json_schema/json_schema.dart'; +import 'package:json_schema/src/json_schema/typedefs.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; + +/// The globally configured json shema class. Any json schema class that is not +/// explicitly given a [JsonSchema] instance upon construction will +/// inherit this global one. +CreateJsonSchemaFromUrl get globalCreateJsonSchemaFromUrl => _globalCreateJsonSchemaFromUrl; +set globalCreateJsonSchemaFromUrl(CreateJsonSchemaFromUrl createJsonSchemaFromUrl) { + if (createJsonSchemaFromUrl == null) { + throw ArgumentError('json_schema: Global createJsonSchemaFromUrl ' + 'implementation must not be null.'); + } + + _globalCreateJsonSchemaFromUrl = createJsonSchemaFromUrl; +} + +CreateJsonSchemaFromUrl _globalCreateJsonSchemaFromUrl; + +/// Reset the globally configured json schema class. +void resetGlobalTransportPlatform() { + _globalCreateJsonSchemaFromUrl = null; +} + +/// Default validators for all [JsonSchema]s. +DefaultValidators get defaultValidators => _defaultValidators ?? DefaultValidators(); +set defaultValidators(DefaultValidators defaultValidators) { + if (defaultValidators == null) { + throw ArgumentError('json_schema: default validators ' + 'implementation must not be null.'); + } + + _defaultValidators = defaultValidators; +} + +DefaultValidators _defaultValidators; diff --git a/lib/src/json_schema/json_schema.dart b/lib/src/json_schema/json_schema.dart new file mode 100644 index 00000000..e123be02 --- /dev/null +++ b/lib/src/json_schema/json_schema.dart @@ -0,0 +1,1689 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:json_pointer/json_pointer.dart'; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/format_exceptions.dart'; +import 'package:json_schema/src/json_schema/global_platform_functions.dart'; +import 'package:json_schema/src/json_schema/ref_provider.dart'; +import 'package:json_schema/src/json_schema/schema_type.dart'; +import 'package:json_schema/src/json_schema/type_validators.dart'; +import 'package:json_schema/src/json_schema/typedefs.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; +import 'package:json_schema/src/json_schema/validator.dart'; + +class RetrievalRequest { + Uri schemaUri; + AsyncRetrievalOperation asyncRetrievalOperation; + SyncRetrievalOperation syncRetrievalOperation; +} + +/// Constructed with a json schema, either as string or Map. Validation of +/// the schema itself is done on construction. Any errors in the schema +/// result in a FormatException being thrown. +class JsonSchema { + JsonSchema._fromMap(this._root, this._schemaMap, this._path, {JsonSchema parent}) { + this._parent = parent; + _initialize(); + } + + JsonSchema._fromBool(this._root, this._schemaBool, this._path, {JsonSchema parent}) { + this._parent = parent; + _initialize(); + } + + JsonSchema._fromRootMap(this._schemaMap, SchemaVersion schemaVersion, + {Uri fetchedFromUri, bool isSync = false, Map refMap, RefProvider refProvider}) { + _initialize( + schemaVersion: schemaVersion, + fetchedFromUri: fetchedFromUri, + isSync: isSync, + refMap: refMap, + refProvider: refProvider, + ); + } + + JsonSchema._fromRootBool(this._schemaBool, SchemaVersion schemaVersion, + {Uri fetchedFromUri, bool isSync = false, Map refMap, RefProvider refProvider}) { + _initialize( + schemaVersion: schemaVersion, + fetchedFromUri: fetchedFromUri, + isSync: isSync, + refMap: refMap, + refProvider: refProvider, + ); + } + + /// Create a schema from a JSON [data]. + /// + /// This method is asynchronous to support automatic fetching of sub-[JsonSchema]s for items, + /// properties, and sub-properties of the root schema. + /// + /// If you want to create a [JsonSchema] synchronously, use [createSchema]. Note that for + /// [createSchema] remote reference fetching is not supported. + /// + /// The [schema] can either be a decoded JSON object (Only [Map] or [bool] per the spec), + /// or alternatively, a [String] may be passed in and JSON decoding will be handled automatically. + static Future createSchemaAsync(dynamic schema, + {SchemaVersion schemaVersion, Uri fetchedFromUri, RefProvider refProvider}) { + // Default to assuming the schema is already a decoded, primitive dart object. + dynamic data = schema; + + /// JSON Schemas must be [bool]s or [Map]s, so if we encounter a [String], we're looking at encoded JSON. + /// https://json-schema.org/latest/json-schema-core.html#rfc.section.4.3.1 + if (schema is String) { + try { + data = json.decode(schema); + } catch (e) { + throw ArgumentError('String data provided to createSchemaAsync is not valid JSON.'); + } + } + + /// Set the Schema version before doing anything else, since almost everything depends on it. + final version = _getSchemaVersion(schemaVersion, data); + + if (data is Map) { + return JsonSchema._fromRootMap(data, schemaVersion, fetchedFromUri: fetchedFromUri, refProvider: refProvider) + ._thisCompleter + .future; + + // Boolean schemas are only supported in draft 6 and later. + } else if (data is bool && version == SchemaVersion.draft6) { + return JsonSchema._fromRootBool(data, schemaVersion, fetchedFromUri: fetchedFromUri, refProvider: refProvider) + ._thisCompleter + .future; + } + throw ArgumentError( + 'Data provided to createSchemaAsync is not valid: Data must be, or parse to a Map (or bool in draft6 or later). | $data'); + } + + /// Create a schema from JSON [data]. + /// + /// This method is synchronous, and doesn't support fetching of remote references, properties, and sub-properties of the + /// schema. If you need remote reference support use [createSchemaAsync]. + /// + /// The [schema] can either be a decoded JSON object (Only [Map] or [bool] per the spec), + /// or alternatively, a [String] may be passed in and JSON decoding will be handled automatically. + static JsonSchema createSchema( + dynamic schema, { + SchemaVersion schemaVersion, + Uri fetchedFromUri, + RefProvider refProvider, + }) { + // Default to assuming the schema is already a decoded, primitive dart object. + dynamic data = schema; + + /// JSON Schemas must be [bool]s or [Map]s, so if we encounter a [String], we're looking at encoded JSON. + /// https://json-schema.org/latest/json-schema-core.html#rfc.section.4.3.1 + if (schema is String) { + try { + data = json.decode(schema); + } catch (e) { + throw ArgumentError('String data provided to createSchema is not valid JSON.'); + } + } + + /// Set the Schema version before doing anything else, since almost everything depends on it. + final version = _getSchemaVersion(schemaVersion, data); + + if (data is Map) { + return JsonSchema._fromRootMap( + data, + schemaVersion, + fetchedFromUri: fetchedFromUri, + isSync: true, + refProvider: refProvider, + ); + + // Boolean schemas are only supported in draft 6 and later. + } else if (data is bool && version == SchemaVersion.draft6) { + return JsonSchema._fromRootBool( + data, + schemaVersion, + fetchedFromUri: fetchedFromUri, + isSync: true, + refProvider: refProvider, + ); + } + throw ArgumentError( + 'Data provided to createSchema is not valid: Data must be a Map or a String that parses to a Map (or bool in draft6 or later). | $data'); + } + + /// Create a schema from a URL. + /// + /// This method is asyncronous to support automatic fetching of sub-[JsonSchema]s for items, + /// properties, and sub-properties of the root schema. + static Future createSchemaFromUrl(String schemaUrl, {SchemaVersion schemaVersion}) { + if (globalCreateJsonSchemaFromUrl == null) { + throw StateError('no globalCreateJsonSchemaFromUrl defined!'); + } + return globalCreateJsonSchemaFromUrl(schemaUrl, schemaVersion: schemaVersion); + } + + /// Construct and validate a JsonSchema. + _initialize({ + SchemaVersion schemaVersion, + Uri fetchedFromUri, + bool isSync = false, + Map refMap, + RefProvider refProvider, + }) { + if (_root == null) { + /// Set the Schema version before doing anything else, since almost everything depends on it. + final version = _getSchemaVersion(schemaVersion, this._schemaMap); + + _root = this; + _isSync = isSync; + _refMap = refMap ?? {}; + _refProvider = refProvider; + _schemaVersion = version; + _fetchedFromUri = fetchedFromUri; + try { + _fetchedFromUriBase = JsonSchemaUtils.getBaseFromFullUri(_fetchedFromUri); + } catch (e) { + // ID base can't be set for schemes other than HTTP(S). + // This is expected behavior. + } + _path = '#'; + final String refString = '${_uri ?? ''}$_path'; + _addSchemaToRefMap(refString, this); + _thisCompleter = Completer(); + } else { + _isSync = _root._isSync; + _refProvider = _root._refProvider; + _schemaVersion = _root.schemaVersion; + _refMap = _root._refMap; + _thisCompleter = _root._thisCompleter; + _schemaAssignments = _root._schemaAssignments; + } + + if (_root._isSync) { + _validateSchemaSync(); + if (_root == this) { + _thisCompleter.complete(this); + } + } else { + _validateSchemaAsync(); + } + } + + /// Calculate, validate and set all properties defined in the JSON Schema spec. + /// + /// Doesn't validate interdependent properties. See [_validateInterdependentProperties] + void _validateAndSetIndividualProperties() { + var accessMap; + // Set the access map based on features used in the currently set version. + if (_root.schemaVersion == SchemaVersion.draft4) { + accessMap = _accessMapV4; + } else { + accessMap = _accessMapV6; + } + + // Iterate over all string keys of the root JSON Schema Map. Calculate, validate and + // set all properties according to spec. + _schemaMap.forEach((k, v) { + /// Get the _set method from the [accessMap] based on the [Map] string key. + final SchemaPropertySetter accessor = accessMap[k]; + if (accessor != null) { + accessor(this, v); + } else { + // Attempt to create a schema out of the custom property and register the ref, but don't error if it's not a valid schema. + _createOrRetrieveSchema('$_path/$k', v, (rhs) { + // Translate ref for schema to include full inheritedUri. + final String refPath = rhs._translateLocalRefToFullUri(Uri.parse(rhs.path)).toString(); + return _refMap[refPath] = rhs; + }, mustBeValid: false); + } + }); + } + + void _validateInterdependentProperties() { + // Check that a minimum is set if both exclusiveMinimum and minimum properties are set. + if (_exclusiveMinimum != null && _minimum == null) + throw FormatExceptions.error('exclusiveMinimum requires minimum'); + + // Check that a minimum is set if both exclusiveMaximum and maximum properties are set. + if (_exclusiveMaximum != null && _maximum == null) + throw FormatExceptions.error('exclusiveMaximum requires maximum'); + } + + /// Validate, calculate and set all properties on the [JsonSchema], included properties + /// that have interdependencies. + void _validateAndSetAllProperties() { + _validateAndSetIndividualProperties(); + + // Interdependent properties were removed in draft6. + if (schemaVersion == SchemaVersion.draft4) { + _validateInterdependentProperties(); + } + } + + void _baseResolvePaths() { + if (_root == this) { + // Validate refs in localRefs. + for (Uri localRef in _localRefs) { + _getSchemaFromPath(localRef); + } + + // Filter out requests that can be resolved locally. + final List requestsToRemove = []; + for (final retrievalRequest in _retrievalRequests) { + // Optimistically assume resolution successful, and switch to false on errors. + bool resolvedSuccessfully = true; + JsonSchema localSchema; + final Uri schemaUri = retrievalRequest.schemaUri; + + // Attempt to resolve schema if it does not exist within ref map already. + if (_refMap[schemaUri.toString()] == null) { + final Uri baseUri = schemaUri.scheme.isNotEmpty ? schemaUri.removeFragment() : schemaUri; + final String baseUriString = '${baseUri}#'; + + if (baseUri == _inheritedUri) { + // If the ref base is the same as the _inheritedUri, ref is _root schema. + localSchema = _root; + } else if (baseUriString != null && _refMap[baseUriString] != null) { + // If the ref base is already in the _refMap, set it directly. + localSchema = _refMap[baseUriString]; + } else if (baseUriString != null && SchemaVersion.fromString(baseUriString) != null) { + // If the referenced URI is or within versioned schema spec. + localSchema = JsonSchema.createSchema(getJsonSchemaDefinitionByRef(baseUriString)); + _addSchemaToRefMap(baseUriString, localSchema); + } else { + // The remote ref needs to be resolved if the above checks failed. + resolvedSuccessfully = false; + } + + // Resolve sub schema of fetched schema if a fragment was included. + if (resolvedSuccessfully && schemaUri.fragment != null && schemaUri.fragment.isNotEmpty) { + try { + localSchema.resolvePath(Uri.parse('#${schemaUri.fragment}')); + } catch (e) { + // If we couldn't resolve the path locally, attempt to make a request anyway. + resolvedSuccessfully = false; + } + } + } + + // Mark retrieval request to be removed since it was resolved. + if (resolvedSuccessfully) { + requestsToRemove.add(retrievalRequest); + } + } + + // Pull out all successful retrieval requests. + requestsToRemove.forEach(_retrievalRequests.remove); + } + } + + /// Check for refs that need to be fetched, fetch them, and return the final [JsonSchema]. + void _resolveAllPathsAsync() async { + if (_root == this) { + if (_retrievalRequests.isNotEmpty) { + await Future.wait(_retrievalRequests.map((r) => r.asyncRetrievalOperation())); + } + + _schemaAssignments.forEach((assignment) => assignment()); + _thisCompleter.complete(_getSchemaFromPath(Uri.parse('#'))); + + // _logger.info('Marked $_path complete'); TODO: re-add logger + } + } + + /// Check for refs that need to be fetched, fetch them, and return the final [JsonSchema]. + void _resolveAllPathsSync() { + if (_root == this) { + // If a ref provider is specified, use it and remove the corresponding retrieval request if + // the provider returns a result. + if (_refProvider != null) { + while (_retrievalRequests.isNotEmpty) { + final r = _retrievalRequests.removeAt(0); + if (r.syncRetrievalOperation != null) { + r.syncRetrievalOperation(); + } + } + } + + _schemaAssignments.forEach((assignment) => assignment()); + + // Throw an error if there are any remaining retrieval requests the ref provider couldn't resolve. + if (_retrievalRequests.isNotEmpty) { + throw FormatExceptions.error( + 'When resolving schemas synchronously, all remote refs must be resolvable via a RefProvider. Found ${_retrievalRequests.length} unresolvable request(s): ${_retrievalRequests.map((r) => r.schemaUri).join(',')}'); + } + } + } + + void _validateSchemaBase() { + // _logger.info('Validating schema $_path'); TODO: re-add logger + + if (_isRemoteRef(_schemaMap)) { + // _logger.info('Top level schema is ref: $_schemaRefs'); TODO: re-add logger + } + + _validateAndSetAllProperties(); + } + + /// Validate that a given [JsonSchema] conforms to the official JSON Schema spec. + void _validateSchemaAsync() { + _validateSchemaBase(); + _baseResolvePaths(); + _resolveAllPathsAsync(); + + // _logger.info('Completed Validating schema $_path'); TODO: re-add logger + } + + /// Validate that a given [JsonSchema] conforms to the official JSON Schema spec. + void _validateSchemaSync() { + _validateSchemaBase(); + _baseResolvePaths(); + _resolveAllPathsSync(); + + // _logger.info('Completed Validating schema $_path'); TODO: re-add logger + } + + /// Given a [Uri] path, find the ref'd [JsonSchema] from the map. + JsonSchema _getSchemaFromPath(Uri pathUri, [Set refsEncountered]) { + // Store encountered refs to avoid cycles. + refsEncountered ??= {}; + + Uri basePathUri; + if (pathUri.host.isEmpty && pathUri.path.isEmpty && (_uri != null || _inheritedUri != null)) { + // Prepend _uri or _inheritedUri to provided [Uri] path if no host or path detected. + pathUri = _translateLocalRefToFullUri(pathUri); + } + + // basePathUri should always have an empty fragment. + basePathUri = Uri.parse('${pathUri.removeFragment()}#'); + + // If _refMap already contains the full pathUri, return the ref'd JsonSchema. + if (_refMap.containsKey(pathUri.toString())) { + return _refMap[pathUri.toString()]; + } + + JsonSchema baseSchema; + if (_refMap.containsKey(basePathUri.toString())) { + // Pull the baseSchema from the _refMap + baseSchema = _refMap[basePathUri.toString()]; + } else { + // If the _refMap does not contain basePathUri, the ref was never resolved during validation. + throw ArgumentError( + 'Failed to get schema for path because the schema file ($basePathUri) could not be found. Unable to resolve path $pathUri'); + } + + // Follow JSON Pointer path of fragments if provided. + if (pathUri.fragment.isNotEmpty) { + final List fragments = Uri.parse(pathUri.fragment).pathSegments; + if (fragments.isNotEmpty) { + // Start at the baseSchema. + JsonSchema currentSchema = baseSchema; + + // Iterate through supported keywords or custom properties. + for (int i = 0; i < fragments.length; i++) { + final String fragment = fragments[i]; + + /// Fetch the property getter from the [_baseAccessGetterMap]. + final SchemaPropertyGetter accessor = _baseAccessGetterMap[fragment]; + if (accessor != null) { + // Get the property off the current schema. + final schemaValues = _baseAccessGetterMap[fragment](currentSchema); + if (schemaValues is JsonSchema) { + // Continue iteration if result is a valid schema. + currentSchema = schemaValues; + } else if (schemaValues is Map) { + // Map properties use the following fragment to fetch the value by key. + i += 1; + String propertyKey = fragments[i]; + if (schemaValues[propertyKey] is! JsonSchema) { + try { + propertyKey = Uri.decodeQueryComponent(propertyKey); + propertyKey = unescape(propertyKey); + } catch (e) {} + } + currentSchema = schemaValues[propertyKey]; + + // Fetched properties must be valid schemas. + if (currentSchema is! JsonSchema) { + throw FormatException( + 'Failed to get schema at path: "$fragment/$propertyKey". Property must be a valid schema : $currentSchema'); + } + } else if (schemaValues is List) { + // List properties use the following fragment to fetch the value by index. + i += 1; + final String propertyIndex = fragments[i]; + try { + final int schemaIndex = int.parse(propertyIndex); + currentSchema = schemaValues[schemaIndex]; + } catch (e) { + throw FormatException( + 'Failed to get schema at path: "$fragment/$propertyIndex". Unable to resolve index.'); + } + + if (currentSchema is! JsonSchema) { + throw FormatException( + 'Failed to get schema at path: "$fragment/$propertyIndex". Property must be a valid schema : $currentSchema'); + } + } + } else { + // Fragment might be a custom property, pull from _refMap and throw if result is not a valid JsonSchema. + + // If the currentSchema does not have a _uri set from refProvider, check _refMap for fragment only. + String currentSchemaRefPath = pathUri.toString(); + if (currentSchema._uri == null && currentSchema._inheritedUri == null) { + currentSchemaRefPath = '#${pathUri.fragment}'; + } + currentSchema = currentSchema._refMap[currentSchemaRefPath]; + if (currentSchema is! JsonSchema) { + throw FormatException( + 'Failed to get schema at path: "$fragment". Custom property must be a valid schema, but got : $currentSchema'); + } + } + + // If currentSchema is a ref, resolve ref recursively. + if (currentSchema.ref != null) { + if (!refsEncountered.add(currentSchema.ref)) { + // Throw if cycle is detected for currentSchema ref. + throw FormatException('Failed to get schema at path: "${currentSchema.ref}". Cycle detected.'); + } + + currentSchema = currentSchema._getSchemaFromPath(currentSchema.ref, refsEncountered); + if (currentSchema == null) { + throw ArgumentError( + 'Failed to get schema at path: "$pathUri". Can\'t resolve reference within the schema.'); + } + } + } + + // Return the successfully resolved schema from fragment path. + return currentSchema; + } + } + + // No fragments present, return the successfully resolved base schema. + return baseSchema; + } + + /// Create a sub-schema inside the root, using either a directly nested schema, or a definition. + JsonSchema _createSubSchema(dynamic schemaDefinition, String path) { + if (schemaDefinition is Map) { + return JsonSchema._fromMap(_root, schemaDefinition, path, parent: this); + + // Boolean schemas are only supported in draft 6 and later. + } else if (schemaDefinition is bool && schemaVersion == SchemaVersion.draft6) { + return JsonSchema._fromBool(_root, schemaDefinition, path, parent: this); + } + throw ArgumentError( + 'Data provided to createSubSchema is not valid: Must be a Map (or bool in draft6 or later). | ${schemaDefinition}'); + } + + JsonSchema _checkRefMapForRef(Uri ref) { + final Uri baseUri = ref.removeFragment(); + + // Always check refMap first. + if (_refMap.containsKey('$baseUri#')) { + final JsonSchema baseSchema = _refMap['$baseUri#']; + if (ref.fragment.isNotEmpty) { + return baseSchema.resolvePath(Uri.parse('#${ref.fragment}')); + } + + return baseSchema; + } + + return null; + } + + JsonSchema _fetchRefSchemaFromSyncProvider(Uri ref) { + final Uri baseUri = ref.removeFragment(); + + final JsonSchema refMapResult = _checkRefMapForRef(ref); + if (refMapResult != null) return refMapResult; + + // Fallback order for ref provider: + // 1. Base URI (example: localhost:1234/integer.json) + // 2. Base URI with empty fragment (example: localhost:1234/integer.json#) + final dynamic schemaDefinition = _refProvider.provide(baseUri.toString()) ?? _refProvider.provide('${baseUri}#'); + + return _createAndResolveProvidedSchema(ref, schemaDefinition); + } + + Future _fetchRefSchemaFromAsyncProvider(Uri ref) async { + final Uri baseUri = ref.removeFragment(); + + final JsonSchema refMapResult = _checkRefMapForRef(ref); + if (refMapResult != null) return refMapResult; + + // Fallback order for ref provider: + // 1. Base URI (example: localhost:1234/integer.json) + // 2. Base URI with empty fragment (example: localhost:1234/integer.json#) + final dynamic schemaDefinition = + await _refProvider.provide(baseUri.toString()) ?? await _refProvider.provide('${baseUri}#'); + + return _createAndResolveProvidedSchema(ref, schemaDefinition); + } + + JsonSchema _createAndResolveProvidedSchema(Uri ref, dynamic schemaDefinition) { + final Uri baseUri = ref.removeFragment(); + + JsonSchema baseSchema; + if (schemaDefinition is JsonSchema) { + // Provider gave validated schema. + baseSchema = schemaDefinition; + } else if (schemaDefinition is Map) { + // Provider gave a schema object. + baseSchema = JsonSchema._fromRootMap( + schemaDefinition, + schemaVersion, + isSync: _isSync, + refMap: _refMap, + refProvider: _refProvider, + fetchedFromUri: baseUri, + ); + _addSchemaToRefMap(baseSchema._uri.toString(), baseSchema); + } else if (schemaDefinition is bool && schemaVersion == SchemaVersion.draft6) { + baseSchema = JsonSchema._fromRootBool( + schemaDefinition, + schemaVersion, + isSync: _isSync, + refMap: _refMap, + refProvider: _refProvider, + fetchedFromUri: baseUri, + ); + _addSchemaToRefMap(baseSchema._uri.toString(), baseSchema); + } + + if (baseSchema != null && ref.hasFragment && ref.fragment.isNotEmpty) { + // Resolve fragment if provided. + return baseSchema.resolvePath(Uri.parse('#${ref.fragment}')); + } + + // Return base schema or null if no fragment present. + return baseSchema; + } + + // -------------------------------------------------------------------------- + // Root Schema Fields + // -------------------------------------------------------------------------- + + /// The root [JsonSchema] for this [JsonSchema]. + JsonSchema _root; + + /// The parent [JsonSchema] for this [JsonSchema]. + JsonSchema _parent; + + /// JSON of the [JsonSchema] as a [Map]. Only this value or [_schemaBool] should be set, not both. + Map _schemaMap = {}; + + /// JSON of the [JsonSchema] as a [bool]. Only this value or [_schemaMap] should be set, not both. + bool _schemaBool; + + /// JSON Schema version string. + SchemaVersion _schemaVersion; + + /// Remote [Uri] the [JsonSchema] was fetched from, if any. + Uri _fetchedFromUri; + + /// Base of the remote [Uri] the [JsonSchema] was fetched from, if any. + Uri _fetchedFromUriBase; + + /// A [List] which the value must conform to all of. + List _allOf = []; + + /// A [List] which the value must conform to at least one of. + List _anyOf = []; + + /// Whether or not const is set, we need this since const can be null and valid. + bool _hasConst = false; + + /// A value which the [JsonSchema] instance must exactly conform to. + dynamic _constValue; + + /// Default value of the [JsonSchema]. + dynamic _defaultValue; + + /// Included [JsonSchema] definitions. + Map _definitions = {}; + + /// Description of the [JsonSchema]. + String _description; + + /// Possible values of the [JsonSchema]. + List _enumValues = []; + + /// Example values for the given schema. + List _examples = []; + + /// Whether the maximum of the [JsonSchema] is exclusive. + bool _exclusiveMaximum; + + /// Whether the maximum of the [JsonSchema] is exclusive. + num _exclusiveMaximumV6; + + /// Whether the minumum of the [JsonSchema] is exclusive. + bool _exclusiveMinimum; + + /// Whether the minumum of the [JsonSchema] is exclusive. + num _exclusiveMinimumV6; + + /// Pre-defined format (i.e. date-time, email, etc) of the [JsonSchema] value. + String _format; + + /// ID of the [JsonSchema]. + Uri _id; + + /// Base URI of the ID. All sub-schemas are resolved against this + Uri _idBase; + + /// Maximum value of the [JsonSchema] value. + num _maximum; + + /// Minimum value of the [JsonSchema] value. + num _minimum; + + /// Maximum value of the [JsonSchema] value. + int _maxLength; + + /// Minimum length of the [JsonSchema] value. + int _minLength; + + /// The number which the value of the [JsonSchema] must be a multiple of. + num _multipleOf; + + /// A [JsonSchema] which the value must NOT be. + JsonSchema _notSchema; + + /// A [List] which the value must conform to at least one of. + List _oneOf = []; + + /// The regular expression the [JsonSchema] value must conform to. + RegExp _pattern; + + /// Ref to the URI of the [JsonSchema]. + Uri _ref; + + /// The path of the [JsonSchema] within the root [JsonSchema]. + String _path; + + /// Title of the [JsonSchema]. + String _title; + + /// List of allowable types for the [JsonSchema]. + List _typeList; + + // -------------------------------------------------------------------------- + // Schema List Item Related Fields + // -------------------------------------------------------------------------- + + /// [JsonSchema] definition used to validate items of this schema. + JsonSchema _items; + + /// List of [JsonSchema] used to validate items of this schema. + List _itemsList; + + /// Whether additional items are allowed. + bool _additionalItemsBool; + + /// [JsonSchema] additionalItems should conform to. + JsonSchema _additionalItemsSchema; + + /// [JsonSchema] definition that at least one item must match to be valid. + JsonSchema _contains; + + /// Maimum number of items allowed. + int _maxItems; + + /// Maimum number of items allowed. + int _minItems; + + /// Whether the items in the list must be unique. + bool _uniqueItems = false; + + // -------------------------------------------------------------------------- + // Schema Sub-Property Related Fields + // -------------------------------------------------------------------------- + + /// Map of [JsonSchema]s by property key. + Map _properties = {}; + + /// [JsonSchema] that property names must conform to. + JsonSchema _propertyNamesSchema; + + /// Whether additional properties, other than those specified, are allowed. + bool _additionalProperties; + + /// [JsonSchema] that additional properties must conform to. + JsonSchema _additionalPropertiesSchema; + + Map> _propertyDependencies = {}; + + Map _schemaDependencies = {}; + + /// The maximum number of properties allowed. + int _maxProperties; + + /// The minimum number of properties allowed. + int _minProperties = 0; + + /// Map of [JsonSchema]s for properties, based on [RegExp]s keys. + Map _patternProperties = {}; + + /// Map of sub-properties' and references' [JsonSchema]s by path. + Map _refMap = {}; + + /// List if properties that are required for the [JsonSchema] instance to be valid. + List _requiredProperties; + + // -------------------------------------------------------------------------- + // Implementation Specific Fields + // -------------------------------------------------------------------------- + + /// Set of local ref Uris to validate during ref resolution. + Set _localRefs = Set(); + + /// HTTP(S) requests to fetch ref'd schemas. + List _retrievalRequests = []; + + /// Remote ref assignments that need to wait until RetrievalRequests have been resolved to execute. + /// + /// The assignments themselves can be thought of as a callback dependent on a Future. + List _schemaAssignments = []; + + /// Completer that fires when [this] [JsonSchema] has finished building. + Completer _thisCompleter = Completer(); + + bool _isSync = false; + + /// Ref provider object used to resolve remote references in a given [JsonSchema]. + /// + /// If [isSync] is true, the provider will be used to fetch remote refs. + /// If [isSync] is false, the provider will be used if specified, otherwise the default HTTP(S) ref provider will be used. + /// If provider type is [RefProviderType.schema], fully resolved + validated schemas are expected from the provider. + /// If provider type is [RefProviderType.json], the provider expects valid JSON objects from the provider. + RefProvider _refProvider; + + static Map _baseAccessGetterMap = { + 'definitions': (JsonSchema s) => s.definitions, + 'properties': (JsonSchema s) => s.properties, + 'items': (JsonSchema s) => s.items ?? s.itemsList, + }; + + /// Shared keywords across all versions of JSON Schema. + static Map _baseAccessMap = { + // Root Schema Properties + 'allOf': (JsonSchema s, dynamic v) => s._setAllOf(v), + 'anyOf': (JsonSchema s, dynamic v) => s._setAnyOf(v), + 'default': (JsonSchema s, dynamic v) => s._setDefault(v), + 'definitions': (JsonSchema s, dynamic v) => s._setDefinitions(v), + 'description': (JsonSchema s, dynamic v) => s._setDescription(v), + 'enum': (JsonSchema s, dynamic v) => s._setEnum(v), + 'format': (JsonSchema s, dynamic v) => s._setFormat(v), + 'maximum': (JsonSchema s, dynamic v) => s._setMaximum(v), + 'minimum': (JsonSchema s, dynamic v) => s._setMinimum(v), + 'maxLength': (JsonSchema s, dynamic v) => s._setMaxLength(v), + 'minLength': (JsonSchema s, dynamic v) => s._setMinLength(v), + 'multipleOf': (JsonSchema s, dynamic v) => s._setMultipleOf(v), + 'not': (JsonSchema s, dynamic v) => s._setNot(v), + 'oneOf': (JsonSchema s, dynamic v) => s._setOneOf(v), + 'pattern': (JsonSchema s, dynamic v) => s._setPattern(v), + '\$ref': (JsonSchema s, dynamic v) => s._setRef(v), + 'title': (JsonSchema s, dynamic v) => s._setTitle(v), + 'type': (JsonSchema s, dynamic v) => s._setType(v), + // Schema List Item Related Fields + 'items': (JsonSchema s, dynamic v) => s._setItems(v), + 'additionalItems': (JsonSchema s, dynamic v) => s._setAdditionalItems(v), + 'maxItems': (JsonSchema s, dynamic v) => s._setMaxItems(v), + 'minItems': (JsonSchema s, dynamic v) => s._setMinItems(v), + 'uniqueItems': (JsonSchema s, dynamic v) => s._setUniqueItems(v), + // Schema Sub-Property Related Fields + 'properties': (JsonSchema s, dynamic v) => s._setProperties(v), + 'additionalProperties': (JsonSchema s, dynamic v) => s._setAdditionalProperties(v), + 'dependencies': (JsonSchema s, dynamic v) => s._setDependencies(v), + 'maxProperties': (JsonSchema s, dynamic v) => s._setMaxProperties(v), + 'minProperties': (JsonSchema s, dynamic v) => s._setMinProperties(v), + 'patternProperties': (JsonSchema s, dynamic v) => s._setPatternProperties(v), + // $schema property always gets set separately, no action required. + '\$schema': (JsonSchema s, dynamic v) => null, + }; + + /// Map to allow getters to be accessed by String key. + static Map _accessMapV4 = Map() + ..addAll(_baseAccessMap) + ..addAll({ + // Add properties that are changed incompatibly later. + 'exclusiveMaximum': (JsonSchema s, dynamic v) => s._setExclusiveMaximum(v), + 'exclusiveMinimum': (JsonSchema s, dynamic v) => s._setExclusiveMinimum(v), + 'id': (JsonSchema s, dynamic v) => s._setId(v), + 'required': (JsonSchema s, dynamic v) => s._setRequired(v), + }); + + static Map _accessMapV6 = Map() + ..addAll(_baseAccessMap) + ..addAll({ + // Note: see http://json-schema.org/draft-06/json-schema-release-notes.html + + // Added in draft6 + 'const': (JsonSchema s, dynamic v) => s._setConst(v), + 'contains': (JsonSchema s, dynamic v) => s._setContains(v), + 'examples': (JsonSchema s, dynamic v) => s._setExamples(v), + 'propertyNames': (JsonSchema s, dynamic v) => s._setPropertyNames(v), + // changed (imcompatible) in draft6 + 'exclusiveMaximum': (JsonSchema s, dynamic v) => s._setExclusiveMaximumV6(v), + 'exclusiveMinimum': (JsonSchema s, dynamic v) => s._setExclusiveMinimumV6(v), + '\$id': (JsonSchema s, dynamic v) => s._setId(v), + 'required': (JsonSchema s, dynamic v) => s._setRequiredV6(v), + }); + + /// Get a nested [JsonSchema] from a path. + JsonSchema resolvePath(Uri path) => _getSchemaFromPath(path); + + @override + bool operator ==(dynamic other) => other is JsonSchema && DeepCollectionEquality().equals(schemaMap, other.schemaMap); + + @override + int get hashCode => DeepCollectionEquality().hash(schemaMap); + + @override + String toString() => '${_schemaBool ?? _schemaMap}'; + + // -------------------------------------------------------------------------- + // Root Schema Getters + // -------------------------------------------------------------------------- + + /// The root [JsonSchema] for this [JsonSchema]. + JsonSchema get root => _root; + + /// The parent [JsonSchema] for this [JsonSchema]. + JsonSchema get parent => _parent; + + /// Get the anchestry of the current schema, up to the root [JsonSchema]. + List get _parents { + final parents = []; + + var circularRefEscapeHatch = 0; + var nextParent = this._parent; + while (nextParent != null && circularRefEscapeHatch < 100) { + circularRefEscapeHatch += 1; + parents.add(nextParent); + + nextParent = nextParent._parent; + } + + return parents; + } + + /// JSON of the [JsonSchema] as a [Map]. Only this value or [_schemaBool] should be set, not both. + Map get schemaMap => _schemaMap; + + /// JSON of the [JsonSchema] as a [bool]. Only this value or [_schemaMap] should be set, not both. + bool get schemaBool => _schemaBool; + + /// JSON string represenatation of the schema. + String toJson() => json.encode(_schemaMap ?? _schemaBool); + + /// JSON Schema version used. + /// + /// Note: Only one version can be used for a nested [JsonScehema] object. + /// Default: [SchemaVersion.draft6] + SchemaVersion get schemaVersion => _root._schemaVersion ?? SchemaVersion.draft6; + + /// Base [Uri] of the [JsonSchema] based on $id, or where it was fetched from, in that order, if any. + Uri get _uriBase => _idBase ?? _fetchedFromUriBase; + + /// [Uri] from the first ancestor with an ID + Uri get _inheritedUriBase { + for (final parent in _parents) { + if (parent._uriBase != null) { + return parent._uriBase; + } + } + + return root._uriBase; + } + + /// [Uri] of the [JsonSchema] based on $id, or where it was fetched from, in that order, if any. + Uri get _uri => ((_id ?? _fetchedFromUri)?.removeFragment()); + + /// [Uri] from the first ancestor with an ID + Uri get _inheritedUri { + for (final parent in _parents) { + if (parent._uri != null) { + return parent._uri; + } + } + + return root._uri; + } + + /// Whether or not const is set, we need this since const can be null and valid. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.24 + bool get hasConst => _hasConst; + + /// Const value of the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.24 + dynamic get constValue => _constValue; + + /// Default value of the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-7.3 + dynamic get defaultValue => _defaultValue; + + /// Included [JsonSchema] definitions. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-7.1 + Map get definitions => _definitions; + + /// Description of the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-7.2 + String get description => _description; + + /// Possible values of the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.23 + List get enumValues => _enumValues; + + /// The value of the exclusiveMaximum for the [JsonSchema], if any exists. + num get exclusiveMaximum { + // If we're on draft6, the property contains the value, return it. + if (schemaVersion == SchemaVersion.draft6) { + return _exclusiveMaximumV6; + + // If we're on draft4, the property is a bool, so return the max instead. + } else { + if (hasExclusiveMaximum) { + return _maximum; + } + } + return null; + } + + /// Whether the maximum of the [JsonSchema] is exclusive. + bool get hasExclusiveMaximum => _exclusiveMaximum ?? _exclusiveMaximumV6 != null; + + /// The value of the exclusiveMaximum for the [JsonSchema], if any exists. + num get exclusiveMinimum { + // If we're on draft6, the property contains the value, return it. + if (schemaVersion == SchemaVersion.draft6) { + return _exclusiveMinimumV6; + + // If we're on draft4, the property is a bool, so return the min instead. + } else { + if (hasExclusiveMinimum) { + return _minimum; + } + } + return null; + } + + /// Whether the minimum of the [JsonSchema] is exclusive. + bool get hasExclusiveMinimum => _exclusiveMinimum ?? _exclusiveMinimumV6 != null; + + /// Pre-defined format (i.e. date-time, email, etc) of the [JsonSchema] value. + String get format => _format; + + /// ID of the [JsonSchema]. + Uri get id => _id; + + /// ID from the first ancestor with an ID + Uri get _inheritedId { + for (final parent in _parents) { + if (parent.id != null) { + return parent.id; + } + } + + return root.id; + } + + /// Maximum value of the [JsonSchema] value. + /// + /// Reference: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.2 + num get maximum => _maximum; + + /// Minimum value of the [JsonSchema] value. + /// + /// Reference: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.4 + num get minimum => _minimum; + + /// Maximum length of the [JsonSchema] value. + /// + /// Reference: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.6 + int get maxLength => _maxLength; + + /// Minimum length of the [JsonSchema] value. + /// + /// Reference: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.7 + int get minLength => _minLength; + + /// The number which the value of the [JsonSchema] must be a multiple of. + /// + /// Reference: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.1 + num get multipleOf => _multipleOf; + + /// The path of the [JsonSchema] within the root [JsonSchema]. + String get path => _path; + + /// The regular expression the [JsonSchema] value must conform to. + /// + /// Refernce: + RegExp get pattern => _pattern; + + /// A [List] which the value must conform to all of. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.26 + List get allOf => _allOf; + + /// A [List] which the value must conform to at least one of. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.27 + List get anyOf => _anyOf; + + /// A [List] which the value must conform to at least one of. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.28 + List get oneOf => _oneOf; + + /// A [JsonSchema] which the value must NOT be. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.29 + JsonSchema get notSchema => _notSchema; + + /// Ref to the URI of the [JsonSchema]. + Uri get ref => _ref; + + /// Title of the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-7.2 + String get title => _title; + + /// List of allowable types for the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.25 + List get typeList => _typeList; + + /// Single allowable type for the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.25 + SchemaType get type => _typeList.length == 1 ? _typeList.single : null; + + // -------------------------------------------------------------------------- + // Schema List Item Related Getters + // -------------------------------------------------------------------------- + + /// Single [JsonSchema] sub items of this [JsonSchema] must conform to. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.9 + JsonSchema get items => _items; + + /// Ordered list of [JsonSchema] which the value of the same index must conform to. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.9 + List get itemsList => _itemsList; + + /// Whether additional items are allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.10 + bool get additionalItemsBool => _additionalItemsBool; + + /// JsonSchema additional items should conform to. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.10 + JsonSchema get additionalItemsSchema => _additionalItemsSchema; + + /// List of example instances for the [JsonSchema]. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-7.4 + List get examples => defaultValue != null + ? ([] + ..addAll(_examples) + ..add(defaultValue)) + : _examples; + + /// The maximum number of items allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.11 + int get maxItems => _maxItems; + + /// The minimum number of items allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.12 + int get minItems => _minItems; + + /// Whether the items in the list must be unique. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.13 + bool get uniqueItems => _uniqueItems; + + // -------------------------------------------------------------------------- + // Schema Sub-Property Related Getters + // -------------------------------------------------------------------------- + + /// Map of [JsonSchema]s for properties, by [String] key. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.18 + Map get properties => _properties; + + /// [JsonSchema] that property names must conform to. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.22 + JsonSchema get propertyNamesSchema => _propertyNamesSchema; + + /// Whether additional properties, other than those specified, are allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.20 + bool get additionalPropertiesBool => _additionalProperties; + + /// [JsonSchema] that additional properties must conform to. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.20 + JsonSchema get additionalPropertiesSchema => _additionalPropertiesSchema; + + /// [JsonSchema] definition that at least one item must match to be valid. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.14 + JsonSchema get contains => _contains; + + /// The maximum number of properties allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.15 + int get maxProperties => _maxProperties; + + /// The minimum number of properties allowed. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.16 + int get minProperties => _minProperties; + + /// Map of [JsonSchema]s for properties, based on [RegExp]s keys. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.19 + Map get patternProperties => _patternProperties; + + /// Map of property dependencies by property name. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.21 + Map> get propertyDependencies => _propertyDependencies; + + /// Map of sub-properties' and references' [JsonSchema]s by path. + /// + /// Note: This is useful for drawing dependency graphs, etc, but should not be used for general + /// validation or traversal. Use [endPath] to get the absolute [String] path and [resolvePath] + /// to get the [JsonSchema] at any path, instead. + @Deprecated(''' + Note: This information is useful for drawing dependency graphs, etc, but should not be used for general + validation or traversal. Use [endPath] to get the absolute [String] path and [resolvePath] + to get the [JsonSchema] at any path, instead. + ''') + Map get refMap => _refMap; + + /// Properties that must be inclueded for the [JsonSchema] to be valid. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.17 + List get requiredProperties => _requiredProperties; + + /// Map of schema dependencies by property name. + /// + /// Spec: https://tools.ietf.org/html/draft-wright-json-schema-validation-01#section-6.21 + Map get schemaDependencies => _schemaDependencies; + + // -------------------------------------------------------------------------- + // Convenience Methods + // -------------------------------------------------------------------------- + + void _addRefRetrievals(Uri ref) { + final addSchemaFunction = (JsonSchema schema) { + if (schema != null) { + // Set referenced schema's path should be equivalent to the $ref value. + // Otherwise it's set as `/`, which doesn't help track down + // the source of validation errors. + schema._path = ref.toString() + '/'; + + final String rootRef = '${ref.removeFragment()}#'; + _addSchemaToRefMap(rootRef, schema._root); + } else { + String exceptionMessage = 'Couldn\'t resolve ref: ${ref} '; + if (_refProvider != null) { + exceptionMessage += 'using the provided ref provider'; + } else { + exceptionMessage += _isSync ? 'due to null ref provider' : 'using the default HTTP(S) ref provider'; + } + throw FormatExceptions.error(exceptionMessage); + } + }; + + final AsyncRetrievalOperation asyncRefSchemaOperation = _refProvider != null + ? () => _fetchRefSchemaFromAsyncProvider(ref).then(addSchemaFunction) + : () => createSchemaFromUrl(ref.toString()).then(addSchemaFunction); + + final SyncRetrievalOperation syncRefSchemaOperation = + _refProvider != null ? () => addSchemaFunction(_fetchRefSchemaFromSyncProvider(ref)) : null; + + /// Always add sub-schema retrieval requests to the [_root], as this is where the promise resolves. + _root._retrievalRequests.add(RetrievalRequest() + ..schemaUri = ref + ..asyncRetrievalOperation = asyncRefSchemaOperation + ..syncRetrievalOperation = syncRefSchemaOperation); + } + + /// Prepends inherited Uri data to the ref if necessary. + Uri _translateLocalRefToFullUri(Uri ref) { + // TODO: add a more advanced check to find out if the $ref is local. + // Does it have a fragment? Append the base and check if it exists in the _refMap + // Does it have a path? Append the base and check if it exists in the _refMap + if (ref.scheme.isEmpty) { + /// If the ref has a path, append it to the inheritedUriBase + if (ref.path != null && ref.path != '/' && ref.path.isNotEmpty) { + final String path = ref.path.startsWith('/') ? ref.path : '/${ref.path}'; + String template = '${_uriBase ?? _inheritedUriBase ?? ''}$path'; + + if (ref.fragment != null && ref.fragment.isNotEmpty) { + template += '#${ref.fragment}'; + } + ref = Uri.parse(template); + } else if (ref.fragment != null) { + // If the ref has a fragment, append it to the _uri or _inheritedUri, or use it alone. + ref = Uri.parse('${_uri ?? _inheritedUri ?? ''}#${ref.fragment}'); + } + } + + return ref; + } + + /// Name of the property of the current [JsonSchema] within its parent. + String get propertyName { + final pathUri = Uri.parse(path); + final pathFragments = pathUri.fragment?.split('/'); + return pathFragments.length > 2 ? pathFragments.last : null; + } + + /// Whether a given property is required for the [JsonSchema] instance to be valid. + bool propertyRequired(String property) => _requiredProperties != null && _requiredProperties.contains(property); + + /// Whether the [JsonSchema] is required on its parent. + bool get requiredOnParent => _parent?.propertyRequired(propertyName) ?? false; + + /// Validate [instance] against this schema, returning a boolean indicating whether + /// validation succeeded or failed. + bool validate(dynamic instance, {bool reportMultipleErrors = false, bool parseJson = false}) => + Validator(this).validate(instance, reportMultipleErrors: reportMultipleErrors, parseJson: parseJson); + + /// Validate [instance] against this schema, returning a list of [ValidationError] + /// objects with information about any validation errors that occurred. + List validateWithErrors(dynamic instance, {bool parseJson = false}) { + final validator = Validator(this); + validator.validate(instance, reportMultipleErrors: true, parseJson: parseJson); + return validator.errorObjects; + } + + // -------------------------------------------------------------------------- + // JSON Schema Internal Operations + // -------------------------------------------------------------------------- + + /// Function to determine whether a given [schemaDefinition] is a remote $ref. + bool _isRemoteRef(dynamic schemaDefinition) { + Map schemaDefinitionMap; + try { + schemaDefinitionMap = TypeValidators.object('NA', schemaDefinition); + } catch (e) { + // If the schema definition isn't an object, return early, since it can't be a ref. + return false; + } + + if (schemaDefinitionMap[r'$ref'] is String) { + final String ref = schemaDefinitionMap[r'$ref']; + if (ref != null) { + TypeValidators.nonEmptyString(r'$ref', ref); + // If the ref begins with "#" it is a local ref, so we return false. + if (ref.startsWith('#')) return false; + return true; + } + } + return false; + } + + /// Add a ref'd JsonSchema to the map of available Schemas. + JsonSchema _addSchemaToRefMap(String path, JsonSchema schema) => _refMap[path] = schema; + + // Create a [JsonSchema] from a sub-schema of the root. + _createOrRetrieveSchema(String path, dynamic schema, SchemaAssigner assigner, {mustBeValid = true}) { + var throwError; + + if (schema is bool && schemaVersion != SchemaVersion.draft6) + throwError = () => throw FormatExceptions.schema(path, schema); + if (schema is! Map && schema is! bool) throwError = () => throw FormatExceptions.schema(path, schema); + + if (throwError != null) { + if (mustBeValid) throwError(); + return; + } + + final isRemoteReference = _isRemoteRef(schema); + + /// If this sub-schema is a ref within the root schema, + /// add it to the map of local schema assignments. + /// Otherwise, call the assigner function and create a new [JsonSchema]. + if (isRemoteReference) { + final schemaDefinitionMap = TypeValidators.object(path, schema); + Uri ref = TypeValidators.uri(r'$ref', schemaDefinitionMap[r'$ref']); + + // Add any relevant inherited Uri information. + ref = _translateLocalRefToFullUri(ref); + + // Add retrievals to _root schema. + _addRefRetrievals(ref); + + // Schema Assignments get resolved later from the root schema. + _schemaAssignments.add(() => assigner(_getSchemaFromPath(ref))); + } else { + // Sub schema can be created immediately. + assigner(_createSubSchema(schema, path)); + } + } + + // -------------------------------------------------------------------------- + // Internal Property Validators + // -------------------------------------------------------------------------- + + _validateListOfSchema(String key, dynamic value, SchemaAdder schemaAdder) { + TypeValidators.nonEmptyList(key, value); + for (int i = 0; i < value.length; i++) { + _createOrRetrieveSchema('$_path/$key/$i', value[i], (rhs) => schemaAdder(rhs)); + } + } + + // -------------------------------------------------------------------------- + // Root Schema Property Setters + // -------------------------------------------------------------------------- + + /// Validate, calculate and set the value of the 'allOf' JSON Schema prop. + _setAllOf(dynamic value) => _validateListOfSchema('allOf', value, (schema) => _allOf.add(schema)); + + /// Validate, calculate and set the value of the 'anyOf' JSON Schema prop. + _setAnyOf(dynamic value) => _validateListOfSchema('anyOf', value, (schema) => _anyOf.add(schema)); + + /// Validate, calculate and set the value of the 'const' JSON Schema prop. + _setConst(dynamic value) { + _hasConst = true; + _constValue = value; // Any value is valid for const, even null. + } + + /// Validate, calculate and set the value of the 'defaultValue' JSON Schema prop. + _setDefault(dynamic value) => _defaultValue = value; + + /// Validate, calculate and set the value of the 'definitions' JSON Schema prop. + _setDefinitions(dynamic value) => (TypeValidators.object('definition', value)) + .forEach((k, v) => _createOrRetrieveSchema('$_path/definitions/$k', v, (rhs) => _definitions[k] = rhs)); + + /// Validate, calculate and set the value of the 'description' JSON Schema prop. + _setDescription(dynamic value) => _description = TypeValidators.string('description', value); + + /// Validate, calculate and set the value of the 'enum' JSON Schema prop. + _setEnum(dynamic value) => _enumValues = TypeValidators.uniqueList('enum', value); + + /// Validate, calculate and set the value of the 'exclusiveMaximum' JSON Schema prop. + _setExclusiveMaximum(dynamic value) => _exclusiveMaximum = TypeValidators.boolean('exclusiveMaximum', value); + + /// Validate, calculate and set the value of the 'exclusiveMaximum' JSON Schema prop. + _setExclusiveMaximumV6(dynamic value) => _exclusiveMaximumV6 = TypeValidators.number('exclusiveMaximum', value); + + /// Validate, calculate and set the value of the 'exclusiveMinimum' JSON Schema prop. + _setExclusiveMinimum(dynamic value) => _exclusiveMinimum = TypeValidators.boolean('exclusiveMinimum', value); + + /// Validate, calculate and set the value of the 'exclusiveMinimum' JSON Schema prop. + _setExclusiveMinimumV6(dynamic value) => _exclusiveMinimumV6 = TypeValidators.number('exclusiveMinimum', value); + + /// Validate, calculate and set the value of the 'format' JSON Schema prop. + _setFormat(dynamic value) => _format = TypeValidators.string('format', value); + + /// Validate, calculate and set the value of the 'id' JSON Schema prop. + _setId(dynamic value) { + // First, just add the ref directly, as a fallback, and in case the ID has its own + // unique origin (i.e. http://example1.com vs http://example2.com/) + _id = TypeValidators.uri('id', value); + + // If the current schema $id has no scheme. + if (_id.scheme.isEmpty) { + // If the $id has a path and the root has a base, append it to the base. + if (_inheritedUriBase != null && _id.path != null && _id.path != '/' && _id.path.isNotEmpty) { + final path = _id.path.startsWith('/') ? _id.path : '/${_id.path}'; + _id = Uri.parse('${_inheritedUriBase.toString()}$path'); + + // If the $id has a fragment, append it to the base, or use it alone. + } else if (_id.fragment != null && _id.fragment.isNotEmpty) { + _id = Uri.parse('${_inheritedId ?? ''}#${_id.fragment}'); + } + } + + try { + _idBase = JsonSchemaUtils.getBaseFromFullUri(_id); + } catch (e) { + // ID base can't be set for schemes other than HTTP(S). + // This is expected behavior. + } + + // Add the current schema to the ref map by its id, so it can be referenced elsewhere. + final String refMapString = '$_id${_id.hasFragment ? '' : '#'}'; + _addSchemaToRefMap(refMapString, this); + return _id; + } + + /// Validate, calculate and set the value of the 'minimum' JSON Schema prop. + _setMinimum(dynamic value) => _minimum = TypeValidators.number('minimum', value); + + /// Validate, calculate and set the value of the 'maximum' JSON Schema prop. + _setMaximum(dynamic value) => _maximum = TypeValidators.number('maximum', value); + + /// Validate, calculate and set the value of the 'maxLength' JSON Schema prop. + _setMaxLength(dynamic value) => _maxLength = TypeValidators.nonNegativeInt('maxLength', value); + + /// Validate, calculate and set the value of the 'minLength' JSON Schema prop. + _setMinLength(dynamic value) => _minLength = TypeValidators.nonNegativeInt('minLength', value); + + /// Validate, calculate and set the value of the 'multiple' JSON Schema prop. + _setMultipleOf(dynamic value) => _multipleOf = TypeValidators.nonNegativeNum('multiple', value); + + /// Validate, calculate and set the value of the 'not' JSON Schema prop. + _setNot(dynamic value) { + if (value is Map || value is bool && schemaVersion == SchemaVersion.draft6) { + _createOrRetrieveSchema('$_path/not', value, (rhs) => _notSchema = rhs); + } else { + throw FormatExceptions.error('items must be object (or boolean in draft6 and later): $value'); + } + } + + /// Validate, calculate and set the value of the 'oneOf' JSON Schema prop. + _setOneOf(dynamic value) => _validateListOfSchema('oneOf', value, (schema) => _oneOf.add(schema)); + + /// Validate, calculate and set the value of the 'pattern' JSON Schema prop. + _setPattern(dynamic value) => _pattern = RegExp(TypeValidators.string('pattern', value), unicode: true); + + /// Validate, calculate and set the value of the 'propertyNames' JSON Schema prop. + _setPropertyNames(dynamic value) { + if (value is Map || value is bool && schemaVersion == SchemaVersion.draft6) { + _createOrRetrieveSchema('$_path/propertyNames', value, (rhs) => _propertyNamesSchema = rhs); + } else { + throw FormatExceptions.error('items must be object (or boolean in draft6 and later): $value'); + } + } + + /// Validate, calculate and set the value of the '$ref' JSON Schema prop. + _setRef(dynamic value) { + // Add any relevant inherited Uri information. + _ref = _translateLocalRefToFullUri(TypeValidators.uri(r'$ref', value)); + + // The ref's base is a relative file path, so it should be treated as a relative file URI + final isRelativeFileUri = _inheritedUriBase != null && _inheritedUriBase.scheme.isEmpty; + if (_ref.scheme.isNotEmpty || isRelativeFileUri) { + // Add retrievals to _root schema. + _addRefRetrievals(_ref); + } else { + // Add _ref to _localRefs to be validated during schema path resolution. + _root._localRefs.add(_ref); + } + } + + /// Determine which schema version to use. + /// + /// Note: Uses the user specified version first, then the version set on the schema JSON, then the default. + static SchemaVersion _getSchemaVersion(SchemaVersion userSchemaVersion, dynamic schema) { + if (userSchemaVersion != null) { + return TypeValidators.jsonSchemaVersion4Or6(r'$schema', userSchemaVersion.toString()); + } else if (schema is Map && schema[r'$schema'] is String) { + return TypeValidators.jsonSchemaVersion4Or6(r'$schema', schema[r'$schema']); + } + return SchemaVersion.draft6; + } + + /// Validate, calculate and set the value of the 'title' JSON Schema prop. + _setTitle(dynamic value) => _title = TypeValidators.string('title', value); + + /// Validate, calculate and set the value of the 'type' JSON Schema prop. + _setType(dynamic value) => _typeList = TypeValidators.typeList('type', value); + + // -------------------------------------------------------------------------- + // Schema List Item Related Property Setters + // -------------------------------------------------------------------------- + + /// Validate, calculate and set items of the 'pattern' JSON Schema prop that are also [JsonSchema]s. + _setItems(dynamic value) { + if (value is Map || (value is bool && schemaVersion == SchemaVersion.draft6)) { + _createOrRetrieveSchema('$_path/items', value, (rhs) => _items = rhs); + } else if (value is List) { + int index = 0; + _itemsList = List(value.length); + for (int i = 0; i < value.length; i++) { + _createOrRetrieveSchema('$_path/items/${index++}', value[i], (rhs) => _itemsList[i] = rhs); + } + } else { + throw FormatExceptions.error('items must be object or array (or boolean in draft6 and later): $value'); + } + } + + /// Validate, calculate and set the value of the 'additionalItems' JSON Schema prop. + _setAdditionalItems(dynamic value) { + if (value is bool) { + _additionalItemsBool = value; + } else if (value is Map) { + _createOrRetrieveSchema('$_path/additionalItems', value, (rhs) => _additionalItemsSchema = rhs); + } else { + throw FormatExceptions.error('additionalItems must be boolean or object: $value'); + } + } + + /// Validate, calculate and set the value of the 'contains' JSON Schema prop. + _setContains(dynamic value) => _createOrRetrieveSchema('$_path/contains', value, (rhs) => _contains = rhs); + + /// Validate, calculate and set the value of the 'examples' JSON Schema prop. + _setExamples(dynamic value) => _examples = TypeValidators.list('examples', value); + + /// Validate, calculate and set the value of the 'maxItems' JSON Schema prop. + _setMaxItems(dynamic value) => _maxItems = TypeValidators.nonNegativeInt('maxItems', value); + + /// Validate, calculate and set the value of the 'minItems' JSON Schema prop. + _setMinItems(dynamic value) => _minItems = TypeValidators.nonNegativeInt('minItems', value); + + /// Validate, calculate and set the value of the 'uniqueItems' JSON Schema prop. + _setUniqueItems(dynamic value) => _uniqueItems = TypeValidators.boolean('uniqueItems', value); + + // -------------------------------------------------------------------------- + // Schema Sub-Property Related Property Setters + // -------------------------------------------------------------------------- + + /// Validate, calculate and set sub-items or properties of the schema that are also [JsonSchema]s. + _setProperties(dynamic value) => (TypeValidators.object('properties', value)).forEach((property, subSchema) => + _createOrRetrieveSchema('$_path/properties/$property', subSchema, (rhs) => _properties[property] = rhs)); + + /// Validate, calculate and set the value of the 'additionalProperties' JSON Schema prop. + _setAdditionalProperties(dynamic value) { + if (value is bool) { + _additionalProperties = value; + } else if (value is Map) { + _createOrRetrieveSchema('$_path/additionalProperties', value, (rhs) => _additionalPropertiesSchema = rhs); + } else { + throw FormatExceptions.error('additionalProperties must be a bool or valid schema object: $value'); + } + } + + /// Validate, calculate and set the value of the 'dependencies' JSON Schema prop. + _setDependencies(dynamic value) => (TypeValidators.object('dependencies', value)).forEach((k, v) { + if (v is Map || v is bool && schemaVersion == SchemaVersion.draft6) { + _createOrRetrieveSchema('$_path/dependencies/$k', v, (rhs) => _schemaDependencies[k] = rhs); + } else if (v is List) { + // Dependencies must have contents in draft4, but can be empty in draft6 and later + if (schemaVersion == SchemaVersion.draft4) { + if (v.length == 0) throw FormatExceptions.error('property dependencies must be non-empty array'); + } + + final Set uniqueDeps = Set(); + v.forEach((propDep) { + if (propDep is! String) throw FormatExceptions.string('propertyDependency', v); + + if (uniqueDeps.contains(propDep)) throw FormatExceptions.error('property dependencies must be unique: $v'); + + _propertyDependencies.putIfAbsent(k, () => []).add(propDep); + uniqueDeps.add(propDep); + }); + } else { + throw FormatExceptions.error( + 'dependency values must be object or array (or boolean in draft6 and later): $v'); + } + }); + + /// Validate, calculate and set the value of the 'maxProperties' JSON Schema prop. + _setMaxProperties(dynamic value) => _maxProperties = TypeValidators.nonNegativeInt('maxProperties', value); + + /// Validate, calculate and set the value of the 'minProperties' JSON Schema prop. + _setMinProperties(dynamic value) => _minProperties = TypeValidators.nonNegativeInt('minProperties', value); + + /// Validate, calculate and set the value of the 'patternProperties' JSON Schema prop. + _setPatternProperties(dynamic value) => + (TypeValidators.object('patternProperties', value)).forEach((k, v) => _createOrRetrieveSchema( + '$_path/patternProperties/$k', v, (rhs) => _patternProperties[RegExp(k, unicode: true)] = rhs)); + + /// Validate, calculate and set the value of the 'required' JSON Schema prop. + _setRequired(dynamic value) => + _requiredProperties = (TypeValidators.nonEmptyList('required', value))?.map((value) => value as String)?.toList(); + + /// Validate, calculate and set the value of the 'required' JSON Schema prop. + _setRequiredV6(dynamic value) => + _requiredProperties = (TypeValidators.list('required', value))?.map((value) => value as String)?.toList(); +} diff --git a/lib/src/json_schema/ref_provider.dart b/lib/src/json_schema/ref_provider.dart new file mode 100644 index 00000000..aa3c63a1 --- /dev/null +++ b/lib/src/json_schema/ref_provider.dart @@ -0,0 +1,89 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import '../../json_schema.dart'; + +typedef SyncSchemaProvider = JsonSchema Function(String ref); +typedef SyncJsonProvider = Map Function(String ref); +typedef AsyncJsonProvider = Future> Function(String ref); +typedef AsyncSchemaProvider = Future Function(String ref); + +enum RefProviderType { + schema, + json, +} + +class RefProvider { + RefProvider(this.provide, this.type, this.isSync); + + static RefProvider syncSchema(SyncSchemaProvider provider) { + return RefProvider( + provider, + RefProviderType.schema, + true, + ); + } + + static RefProvider asyncSchema(AsyncSchemaProvider provider) { + return RefProvider( + provider, + RefProviderType.schema, + false, + ); + } + + static RefProvider syncJson(SyncJsonProvider provider) { + return RefProvider( + provider, + RefProviderType.json, + true, + ); + } + + static RefProvider asyncJson(AsyncJsonProvider provider) { + return RefProvider( + provider, + RefProviderType.json, + false, + ); + } + + final bool isSync; + final RefProviderType type; + final T provide; +} diff --git a/lib/src/json_schema/schema.dart b/lib/src/json_schema/schema.dart deleted file mode 100644 index 82700714..00000000 --- a/lib/src/json_schema/schema.dart +++ /dev/null @@ -1,627 +0,0 @@ -part of json_schema.json_schema; - -/// Constructed with a json schema, either as string or Map. Validation of -/// the schema itself is done on construction. Any errors in the schema -/// result in a FormatException being thrown. -class Schema { - Schema._fromMap(this._root, this._schemaMap, this._path) { - // custom - - _initialize(); - _addSchema(path, this); - - // end - } - - Schema._fromRootMap(this._schemaMap) { - // custom - - _initialize(); - - // end - } - - Schema get root => _root; - Map get schemaMap => _schemaMap; - String get path => _path; - num get multipleOf => _multipleOf; - num get maximum => _maximum; - num get minimum => _minimum; - int get maxLength => _maxLength; - int get minLength => _minLength; - RegExp get pattern => _pattern; - List get enumValues => _enumValues; - List get allOf => _allOf; - List get anyOf => _anyOf; - List get oneOf => _oneOf; - Schema get notSchema => _notSchema; - Map get definitions => _definitions; - Uri get id => _id; - String get ref => _ref; - String get description => _description; - String get title => _title; - List get schemaTypeList => _schemaTypeList; - - /// To match all items to a schema - Schema get items => _items; - - /// To match each item in array to a schema - List get itemsList => _itemsList; - dynamic get additionalItems => _additionalItems; - int get maxItems => _maxItems; - int get minItems => _minItems; - bool get uniqueItems => _uniqueItems; - List get requiredProperties => _requiredProperties; - int get maxProperties => _maxProperties; - int get minProperties => _minProperties; - Map get properties => _properties; - bool get additionalProperties => _additionalProperties; - Schema get additionalPropertiesSchema => _additionalPropertiesSchema; - Map get patternProperties => _patternProperties; - Map get schemaDependencies => _schemaDependencies; - Map> get propertyDependencies => _propertyDependencies; - dynamic get defaultValue => _defaultValue; - - /// Map of path to schema object - Map get refMap => _refMap; - - // custom - - static Future createSchemaFromUrl(String schemaUrl) { - Uri uri = Uri.parse(schemaUrl); - if (uri.scheme == 'http') { - _logger.info('Getting url $uri'); - return new HttpClient().getUrl(uri).then((HttpClientRequest request) { - request.followRedirects = true; - return request.close(); - }).then((HttpClientResponse response) { - return response - .transform(new convert.Utf8Decoder()) - .join() - .then((schemaText) { - Map map = convert.JSON.decode(schemaText); - return createSchema(map); - }); - }); - } else if (uri.scheme == 'file' || uri.scheme == '') { - return new File(uri.scheme == 'file' ? uri.toFilePath() : schemaUrl) - .readAsString() - .then((text) => createSchema(convert.JSON.decode(text))); - } else { - throw new FormatException( - "Url schemd must be http, file, or empty: $schemaUrl"); - } - } - - /// Create a schema from a [data] - /// Typically [data] is result of JSON.decode(jsonSchemaString) - static Future createSchema(Map data) => - new Schema._fromRootMap(data)._thisCompleter.future; - - /// Validate [instance] against this schema - bool validate(dynamic instance) => new Validator(this).validate(instance); - - bool get exclusiveMaximum => _exclusiveMaximum == null || _exclusiveMaximum; - bool get exclusiveMinimum => _exclusiveMinimum == null || _exclusiveMinimum; - - bool propertyRequired(String property) => - _requiredProperties != null && _requiredProperties.contains(property); - - /// Given path, follow all references to an end path pointing to schema - String endPath(String path) { - _pathsEncountered.clear(); - return _endPath(path); - } - - /// Returns paths of all paths - Set get paths => new Set.from(_schemaRefs.keys)..addAll(_refMap.keys); - - /// Method to find schema from path - Schema resolvePath(String path) { - while (_schemaRefs.containsKey(path)) { - path = _schemaRefs[path]; - } - return _refMap[path]; - } - - FormatException _boolError(String key, dynamic instance) => - _error("$key must be boolean: $instance"); - FormatException _numError(String key, dynamic instance) => - _error("$key must be num: $instance"); - FormatException _intError(String key, dynamic instance) => - _error("$key must be int: $instance"); - FormatException _stringError(String key, dynamic instance) => - _error("$key must be string: $instance"); - FormatException _objectError(String key, dynamic instance) => - _error("$key must be object: $instance"); - FormatException _arrayError(String key, dynamic instance) => - _error("$key must be array: $instance"); - FormatException _schemaError(String key, dynamic instance) => - _error("$key must be valid schema object: $instance"); - - FormatException _error(String msg) { - msg = "$_path: $msg"; - if (logFormatExceptions) _logger.warning(msg); - return new FormatException(msg); - } - - String _requireString(String key, dynamic value) { - if (value is String) return value; - throw _stringError(key, value); - } - - dynamic _requireNonNegative(String key, dynamic value) { - if (value < 0) throw _error("$key must be non-negative: $value"); - return value; - } - - int _requireNonNegativeInt(String key, dynamic value) { - if (value is int) return _requireNonNegative(key, value); - throw _intError(key, value); - } - - _addSchema(String path, Schema schema) => _refMap[path] = schema; - - _getMultipleOf(dynamic value) { - if (value is! num) throw _numError("multiple", value); - if (value <= 0) throw _error("multipleOf must be > 0: $value"); - _multipleOf = value; - } - - _getMaximum(dynamic value) { - if (value is! num) throw _numError("maximum", value); - _maximum = value; - } - - _getExclusiveMaximum(dynamic value) { - if (value is! bool) throw _boolError("exclusiveMaximum", value); - _exclusiveMaximum = value; - } - - _getMinimum(dynamic value) { - if (value is! num) throw _numError("minimum", value); - _minimum = value; - } - - _getExclusiveMinimum(dynamic value) { - if (value is! bool) throw _boolError("exclusiveMinimum", value); - _exclusiveMinimum = value; - } - - _getMaxLength(dynamic value) => - _maxLength = _requireNonNegativeInt('maxLength', value); - _getMinLength(dynamic value) => - _minLength = _requireNonNegativeInt('minLength', value); - - _getPattern(dynamic value) { - if (value is! String) throw _stringError("pattern", value); - _pattern = new RegExp(value); - } - - _makeSchema(String path, dynamic schema, assigner(Schema rhs)) { - if (schema is! Map) throw _schemaError(path, schema); - if (_registerSchemaRef(path, schema)) { - _schemaAssignments.add(() => assigner(_resolvePath(path))); - } else { - assigner(_createSubSchema(schema, path)); - } - } - - _getProperties(dynamic value) { - if (value is Map) { - value.forEach((property, subSchema) => _makeSchema( - "$_path/properties/$property", - subSchema, - (rhs) => _properties[property] = rhs)); - } else { - throw _objectError("properties", value); - } - } - - _getItems(dynamic value) { - if (value is Map) { - _makeSchema("$_path/items", value, (rhs) => _items = rhs); - } else if (value is List) { - int index = 0; - _itemsList = new List(value.length); - for (int i = 0; i < value.length; i++) { - _makeSchema( - "$_path/items/${index++}", value[i], (rhs) => _itemsList[i] = rhs); - } - } else { - throw _error("items must be object or array: $value"); - } - } - - _getAdditionalItems(dynamic value) { - if (value is bool) { - _additionalItems = value; - } else if (value is Map) { - _makeSchema( - "$_path/additionalItems", value, (rhs) => _additionalItems = rhs); - } else { - throw _error("additionalItems must be boolean or object: $value"); - } - } - - _getMaxItems(dynamic value) => - _maxItems = _requireNonNegativeInt('maxItems', value); - _getMinItems(dynamic value) => - _minItems = _requireNonNegativeInt('minItems', value); - _getUniqueItems(dynamic value) { - if (value is! bool) throw _boolError("uniqueItems", value); - _uniqueItems = value; - } - - _getRequired(dynamic value) { - if (value is! List) throw _arrayError("required", value); - if (value.length == 0) - throw _error("required must be a non-empty array: $value"); - _requiredProperties = new List.from(value); - } - - _getMaxProperties(dynamic value) => - _maxProperties = _requireNonNegativeInt('maxProperties', value); - _getMinProperties(dynamic value) => - _minProperties = _requireNonNegativeInt('minProperties', value); - _getAdditionalProperties(dynamic value) { - if (value is bool) { - _additionalProperties = value; - } else if (value is Map) { - _makeSchema("$_path/additionalProperties", value, - (rhs) => _additionalPropertiesSchema = rhs); - } else { - throw _error( - "additionalProperties must be a bool or valid schema object: $value"); - } - } - - _getPatternProperties(dynamic value) { - if (value is! Map) throw _objectError("patternProperties", value); - - value.forEach((k, v) => _makeSchema("$_path/patternProperties/$k", v, - (rhs) => _patternProperties[new RegExp(k)] = rhs)); - } - - _getDependencies(dynamic value) { - if (value is Map) { - value.forEach((k, v) { - if (v is Map) { - _makeSchema("$_path/dependencies/$k", v, - (rhs) => _schemaDependencies[k] = rhs); - } else if (v is List) { - if (v.length == 0) - throw _error("property dependencies must be non-empty array"); - - Set uniqueDeps = new Set(); - v.forEach((propDep) { - if (propDep is! String) throw _stringError("propertyDependency", v); - - if (uniqueDeps.contains(propDep)) - throw _error("property dependencies must be unique: $v"); - - _propertyDependencies.putIfAbsent(k, () => []).add(propDep); - uniqueDeps.add(propDep); - }); - } else { - throw _error("dependency values must be object or array: $v"); - } - }); - } else { - throw _objectError("dependencies", value); - } - } - - _getEnum(dynamic value) { - if (value is List) { - if (value.length == 0) - throw _error("enum must be a non-empty array: $value"); - int i = 0; - value.forEach((v) { - for (int j = i + 1; j < value.length; j++) { - if (_jsonEqual(value[i], value[j])) - throw _error("enum values must be unique: $value [$i]==[$j]"); - } - i++; - _enumValues.add(v); - }); - } else { - throw _arrayError("enum", value); - } - } - - _getType(dynamic value) { - if (value is String) { - _schemaTypeList = [SchemaType.fromString(value)]; - } else if (value is List) { - _schemaTypeList = value.map((v) => SchemaType.fromString(v)).toList(); - } else { - throw _error("type must be string or array: ${value.runtimeType}"); - } - if (_schemaTypeList.contains(null)) throw _error("type(s) invalid $value"); - } - - _requireListOfSchema(String key, dynamic value, schemaAdder(Schema schema)) { - if (value is List) { - if (value.length == 0) throw _error("$key array must not be empty"); - for (int i = 0; i < value.length; i++) { - _makeSchema("$_path/$key/$i", value[i], (rhs) => schemaAdder(rhs)); - } - } else { - throw _arrayError(key, value); - } - } - - _getAllOf(dynamic value) => - _requireListOfSchema("allOf", value, (schema) => _allOf.add(schema)); - _getAnyOf(dynamic value) => - _requireListOfSchema("anyOf", value, (schema) => _anyOf.add(schema)); - _getOneOf(dynamic value) => - _requireListOfSchema("oneOf", value, (schema) => _oneOf.add(schema)); - _getNot(dynamic value) { - if (value is Map) { - _makeSchema("$_path/not", value, (rhs) => _notSchema = rhs); - } else { - throw _objectError("not", value); - } - } - - _getDefinitions(dynamic value) { - if (value is Map) { - value.forEach((k, v) => _makeSchema( - "$_path/definitions/$k", v, (rhs) => _definitions[k] = rhs)); - } else { - throw _objectError("definition", value); - } - } - - _getRef(dynamic value) { - if (value is String) { - _ref = value; - if (_ref.length == 0) throw _error("\$ref must be non-empty string"); - if (_ref[0] != '#') { - var refSchemaFuture = createSchemaFromUrl(_ref) - .then((schema) => _addSchema(_ref, schema)); - _retrievalRequests.add(refSchemaFuture); - } - } else { - throw _stringError(r"$ref", value); - } - } - - _getSchema(dynamic value) { - if (value is String) { - if (value != "http://json-schema.org/draft-04/schema#") { - throw _error("Only draft 4 schema supported"); - } - } else { - throw _stringError(r"$ref", value); - } - } - - _getId(dynamic value) { - if (value is String) { - String id = _requireString("id", value); - try { - _id = Uri.parse(id); - } catch (e) { - throw _error("id must be a valid URI: $value ($e)"); - } - } else { - throw _stringError("id", value); - } - } - - _getTitle(dynamic value) => _title = _requireString("title", value); - _getDescription(dynamic value) => - _description = _requireString("description", value); - _getDefault(dynamic value) => _defaultValue = value; - _getFormat(dynamic value) { - _format = _requireString("format", value); - } - - static Map _accessMap = { - "multipleOf": (s, v) => s._getMultipleOf(v), - "maximum": (s, v) => s._getMaximum(v), - "exclusiveMaximum": (s, v) => s._getExclusiveMaximum(v), - "minimum": (s, v) => s._getMinimum(v), - "exclusiveMinimum": (s, v) => s._getExclusiveMinimum(v), - "maxLength": (s, v) => s._getMaxLength(v), - "minLength": (s, v) => s._getMinLength(v), - "pattern": (s, v) => s._getPattern(v), - "properties": (s, v) => s._getProperties(v), - "maxProperties": (s, v) => s._getMaxProperties(v), - "minProperties": (s, v) => s._getMinProperties(v), - "additionalProperties": (s, v) => s._getAdditionalProperties(v), - "dependencies": (s, v) => s._getDependencies(v), - "patternProperties": (s, v) => s._getPatternProperties(v), - "items": (s, v) => s._getItems(v), - "additionalItems": (s, v) => s._getAdditionalItems(v), - "maxItems": (s, v) => s._getMaxItems(v), - "minItems": (s, v) => s._getMinItems(v), - "uniqueItems": (s, v) => s._getUniqueItems(v), - "required": (s, v) => s._getRequired(v), - "default": (s, v) => s._getDefault(v), - "enum": (s, v) => s._getEnum(v), - "type": (s, v) => s._getType(v), - "allOf": (s, v) => s._getAllOf(v), - "anyOf": (s, v) => s._getAnyOf(v), - "oneOf": (s, v) => s._getOneOf(v), - "not": (s, v) => s._getNot(v), - "definitions": (s, v) => s._getDefinitions(v), - "id": (s, v) => s._getId(v), - "\$ref": (s, v) => s._getRef(v), - "\$schema": (s, v) => s._getSchema(v), - "title": (s, v) => s._getTitle(v), - "description": (s, v) => s._getDescription(v), - "format": (s, v) => s._getFormat(v), - }; - - void _validateSchema() { - _logger.info("Validating schema $_path"); - - if (_registerSchemaRef(_path, _schemaMap)) { - _logger.info("Top level schema is ref: $_schemaRefs"); - } - - _schemaMap.forEach((k, v) { - var accessor = _accessMap[k]; - if (accessor != null) { - accessor(this, v); - } else { - _freeFormMap[PATH.join(_path, _normalizePath(k))] = v; - } - }); - - if (_exclusiveMinimum != null && _minimum == null) - throw _error("exclusiveMinimum requires minimum"); - - if (_exclusiveMaximum != null && _maximum == null) - throw _error("exclusiveMaximum requires maximum"); - - if (_root == this) { - _schemaAssignments.forEach((assignment) => assignment()); - if (_retrievalRequests.isNotEmpty) { - Future - .wait(_retrievalRequests) - .then((_) => _thisCompleter.complete(_resolvePath('#'))); - } else { - _thisCompleter.complete(_resolvePath('#')); - } - _logger.info("Marked $_path complete"); - } - - _logger.info("Completed Validating schema $_path"); - } - - void _initialize() { - if (_root == null) { - _root = this; - _path = '#'; - _addSchema('#', this); - _thisCompleter = new Completer(); - } else { - _schemaRefs = _root._schemaRefs; - _refMap = _root._refMap; - _freeFormMap = _root._freeFormMap; - _thisCompleter = _root._thisCompleter; - _schemaAssignments = _root._schemaAssignments; - } - - _validateSchema(); - } - - String _endPath(String path) { - if (_pathsEncountered.contains(path)) - throw _error("Encountered path cycle ${_pathsEncountered}, adding $path"); - - var referredTo = _schemaRefs[path]; - if (referredTo == null) { - return path; - } else { - _pathsEncountered.add(path); - return _endPath(referredTo); - } - } - - Schema _resolvePath(String original) { - String path = endPath(original); - Schema result = _refMap[path]; - if (result == null) { - var schema = _freeFormMap[path]; - if (schema is! Map) - throw _schemaError("free-form property $original at $path", schema); - return new Schema._fromMap(_root, schema, path); - } - return result; - } - - bool _registerSchemaRef(String path, dynamic schemaDefinition) { - if (schemaDefinition is Map) { - dynamic ref = schemaDefinition[r"$ref"]; - if (ref != null) { - if (ref is String) { - _logger.info("Linking $path to $ref"); - _schemaRefs[path] = ref; - return true; - } else { - throw _stringError("\$ref", ref); - } - } - } - return false; - } - - static String _normalizePath(String path) => - path.replaceAll('~', '~0').replaceAll('/', '~1').replaceAll('%', '%25'); - - Schema _createSubSchema(dynamic schemaDefinition, String path) { - assert(!_schemaRefs.containsKey(path)); - assert(!_refMap.containsKey(path)); - return new Schema._fromMap(_root, schemaDefinition, path); - } - - String toString() => "${_schemaMap}"; - - // end - - Schema _root; - Map _schemaMap = {}; - String _path; - num _multipleOf; - num _maximum; - bool _exclusiveMaximum; - num _minimum; - bool _exclusiveMinimum; - int _maxLength; - int _minLength; - RegExp _pattern; - List _enumValues = []; - List _allOf = []; - List _anyOf = []; - List _oneOf = []; - Schema _notSchema; - Map _definitions = {}; - Uri _id; - String _ref; - String _description; - String _title; - List _schemaTypeList; - Schema _items; - List _itemsList; - dynamic _additionalItems; - int _maxItems; - int _minItems; - bool _uniqueItems = false; - List _requiredProperties; - int _maxProperties; - int _minProperties = 0; - Map _properties = {}; - bool _additionalProperties; - Schema _additionalPropertiesSchema; - Map _patternProperties = {}; - Map _schemaDependencies = {}; - Map> _propertyDependencies = {}; - dynamic _defaultValue; - Map _refMap = {}; - - /// For schemas with $ref maps path of schema to $ref path - Map _schemaRefs = {}; - - /// Assignments to call for resolution upon end of parse - List _schemaAssignments = []; - - /// Maps any non-key top level property to its original value - Map _freeFormMap = {}; - Completer _thisCompleter = new Completer(); - List> _retrievalRequests = []; - - /// Set of strings to gaurd against path cycles - Set _pathsEncountered = new Set(); - - /// Support for optional formats (date-time, uri, email, ipv6, hostname) - String _format; -} - -// custom -// end diff --git a/lib/src/json_schema/schema_type.dart b/lib/src/json_schema/schema_type.dart new file mode 100644 index 00000000..6330e172 --- /dev/null +++ b/lib/src/json_schema/schema_type.dart @@ -0,0 +1,110 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +class SchemaType implements Comparable { + const SchemaType._(this.value); + + static const SchemaType array = SchemaType._(0); + + static const SchemaType boolean = SchemaType._(1); + + static const SchemaType integer = SchemaType._(2); + + static const SchemaType number = SchemaType._(3); + + static const SchemaType nullValue = SchemaType._(4); + + static const SchemaType object = SchemaType._(5); + + static const SchemaType string = SchemaType._(6); + + static List get values => const [array, boolean, integer, number, nullValue, object, string]; + + final int value; + + @override + int get hashCode => value; + + SchemaType copy() => this; + + @override + int compareTo(SchemaType other) => value.compareTo(other.value); + + @override + String toString() { + switch (this) { + case array: + return 'array'; + case boolean: + return 'boolean'; + case integer: + return 'integer'; + case number: + return 'number'; + case nullValue: + return 'null'; + case object: + return 'object'; + case string: + return 'string'; + } + return null; + } + + static SchemaType fromString(String s) { + if (s == null) return null; + switch (s) { + case 'array': + return array; + case 'boolean': + return boolean; + case 'integer': + return integer; + case 'number': + return number; + case 'null': + return nullValue; + case 'object': + return object; + case 'string': + return string; + default: + return null; + } + } +} diff --git a/lib/src/json_schema/type_validators.dart b/lib/src/json_schema/type_validators.dart new file mode 100644 index 00000000..e44a6db6 --- /dev/null +++ b/lib/src/json_schema/type_validators.dart @@ -0,0 +1,104 @@ +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/format_exceptions.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; +import 'package:json_schema/src/json_schema/schema_type.dart'; + +class TypeValidators { + static List list(String key, dynamic value) { + if (value is List) return value; + throw FormatExceptions.list(key, value); + } + + static List nonEmptyList(String key, dynamic value) { + final List theList = list(key, value); + if (theList.length > 0) return theList; + throw FormatExceptions.error('$key must be a non-empty list: $value'); + } + + static List uniqueList(String key, dynamic value) { + int i = 0; + final List enumValues = TypeValidators.nonEmptyList(key, value); + enumValues.forEach((v) { + for (int j = i + 1; j < value.length; j++) { + if (JsonSchemaUtils.jsonEqual(value[i], value[j])) + throw FormatExceptions.error('enum values must be unique: $value [$i]==[$j]'); + } + i++; + }); + return enumValues; + } + + /// Validate a dynamic value is a String. + static String string(String key, dynamic value) { + if (value is String) return value; + throw FormatExceptions.string(key, value); + } + + static String nonEmptyString(String key, dynamic value) { + TypeValidators.string(key, value); + if (value.isNotEmpty) return value; + throw FormatExceptions.error('$key must be a non-empty string: $value'); + } + + static List typeList(String key, dynamic value) { + var typeList; + if (value is String) { + typeList = [SchemaType.fromString(value)]; + } else if (value is List) { + typeList = value.map((v) => SchemaType.fromString(v)).toList(); + } else { + throw FormatExceptions.error('$key must be string or array: ${value.runtimeType}'); + } + if (!typeList.contains(null)) return typeList; + throw FormatExceptions.error('$key(s) invalid $value'); + } + + static dynamic nonNegative(String key, dynamic value) { + if (value < 0) throw FormatExceptions.error('$key must be non-negative: $value'); + return value; + } + + static int nonNegativeInt(String key, dynamic value) { + if (value is int) return nonNegative(key, value); + throw FormatExceptions.int(key, value); + } + + static num number(String key, dynamic value) { + if (value is num) return value; + throw FormatExceptions.num(key, value); + } + + static num nonNegativeNum(String key, dynamic value) { + number(key, value); + if (value > 0) return value; + throw FormatExceptions.nonNegativeNum(key, value); + } + + static bool boolean(String key, dynamic value) { + if (value is bool) return value; + throw FormatExceptions.bool(key, value); + } + + static Map object(String key, dynamic value) { + if (value is Map) return value; + throw FormatExceptions.object(key, value); + } + + static SchemaVersion jsonSchemaVersion4Or6(String key, dynamic value) { + string(key, value); + final schemaVersion = SchemaVersion.fromString(value); + if (schemaVersion != null) { + return schemaVersion; + } + throw FormatExceptions.error('Only draft 4 and draft 6 schemas supported'); + } + + static Uri uri(String key, dynamic value) { + final String id = string('id', value); + try { + return Uri.parse(id); + } catch (e) { + throw FormatExceptions.error('$key must be a valid URI: $value ($e)'); + } + } +} diff --git a/lib/src/json_schema/typedefs.dart b/lib/src/json_schema/typedefs.dart new file mode 100644 index 00000000..88355b47 --- /dev/null +++ b/lib/src/json_schema/typedefs.dart @@ -0,0 +1,50 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:async'; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/json_schema.dart'; + +typedef CreateJsonSchemaFromUrl = Future Function(String schemaUrl, {SchemaVersion schemaVersion}); +typedef SchemaPropertySetter = dynamic Function(JsonSchema s, dynamic value); +typedef SchemaPropertyGetter = dynamic Function(JsonSchema s); +typedef SchemaAssigner = Function(JsonSchema s); +typedef SchemaAdder = Function(JsonSchema s); +typedef AsyncRetrievalOperation = Future Function(); +typedef SyncRetrievalOperation = JsonSchema Function(); diff --git a/lib/src/json_schema/utils.dart b/lib/src/json_schema/utils.dart new file mode 100644 index 00000000..d28350e4 --- /dev/null +++ b/lib/src/json_schema/utils.dart @@ -0,0 +1,128 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'package:uri/uri.dart' show UriTemplate; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/json_schema.dart'; + +class JsonSchemaUtils { + static bool jsonEqual(a, b) { + bool result = true; + if (a is Map && b is Map) { + if (a.length != b.length) return false; + a.keys.forEach((k) { + if (!jsonEqual(a[k], b[k])) { + result = false; + return; + } + }); + } else if (a is List && b is List) { + if (a.length != b.length) return false; + for (int i = 0; i < a.length; i++) { + if (!jsonEqual(a[i], b[i])) { + return false; + } + } + } else { + return a == b; + } + return result; + } + + static String normalizePath(String path) => path.replaceAll('~', '~0').replaceAll('/', '~1').replaceAll('%', '%25'); + + static JsonSchema getSubMapFromFragment(JsonSchema schema, Uri uri) { + if (uri.fragment?.isNotEmpty == true) { + schema = schema.resolvePath(Uri.parse('#${uri.fragment}')); + } + return schema; + } + + static Uri getBaseFromFullUri(Uri uri) { + List segments = []; + if (uri.pathSegments.isNotEmpty /* && uri.pathSegments.last.endsWith('.json')*/) { + segments = []..addAll(uri.pathSegments); + segments.removeLast(); + + return uri.replace(pathSegments: segments); + } + return uri; + } +} + +class DefaultValidators { + emailValidator(String email) => JsonSchemaValidationRegexes.email.firstMatch(email) != null; + + uriValidator(String uri) { + try { + final result = Uri.parse(uri); + // If a URI has no scheme, it is invalid. + if (result.path.startsWith('//') || result.scheme.isEmpty) return false; + // If a URI contains spaces, it is invalid. + if (uri.contains(' ')) return false; + // If a URI contains backslashes, it is invalid + if (uri.contains('\\')) return false; + return true; + } catch (e) { + return false; + } + } + + uriReferenceValidator(String uriReference) { + try { + Uri.parse(uriReference); + // If a URI contains spaces, it is invalid. + if (uriReference.contains(' ')) return false; + // If a URI contains backslashes, it is invalid. + if (uriReference.contains('\\')) return false; + return true; + } catch (e) { + return false; + } + } + + uriTemplateValidator(String uriTemplate) { + try { + UriTemplate(uriTemplate); + return true; + } catch (e) { + return false; + } + } +} diff --git a/lib/src/json_schema/validator.dart b/lib/src/json_schema/validator.dart index 93e3528c..bb8e75f1 100644 --- a/lib/src/json_schema/validator.dart +++ b/lib/src/json_schema/validator.dart @@ -1,425 +1,531 @@ -part of json_schema.json_schema; +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:convert'; +import 'dart:math'; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/json_schema.dart'; +import 'package:json_schema/src/json_schema/schema_type.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; +import 'package:json_schema/src/json_schema/global_platform_functions.dart' show defaultValidators; + +class Instance { + Instance(dynamic data, {String path = ''}) { + this.data = data; + this.path = path; + } + + dynamic data; + String path; + + @override + toString() => data.toString(); +} + +class ValidationError { + ValidationError._(this.instancePath, this.schemaPath, this.message); + + /// Path in the instance data to the key where this error occurred + String instancePath; + + /// Path to the key in the schema containing the rule that produced this error + String schemaPath; + + /// A human-readable message explaining why validation failed + String message; + + @override + toString() => '${instancePath.isEmpty ? '# (root)' : instancePath}: $message'; +} /// Initialized with schema, validates instances against it class Validator { Validator(this._rootSchema); - List get errors => _errors; + List get errors => _errors.map((e) => e.toString()).toList(); - // custom + List get errorObjects => _errors; /// Validate the [instance] against the this validator's schema - bool validate(dynamic instance, [bool reportMultipleErrors = false]) { - _logger - .info("Validating ${instance.runtimeType}:$instance on ${_rootSchema}"); + bool validate(dynamic instance, {bool reportMultipleErrors = false, bool parseJson = false}) { + // _logger.info('Validating ${instance.runtimeType}:$instance on ${_rootSchema}'); TODO: re-add logger + + dynamic data = instance; + if (parseJson && instance is String) { + try { + data = json.decode(instance); + } catch (e) { + throw ArgumentError('JSON instance provided to validate is not valid JSON.'); + } + } _reportMultipleErrors = reportMultipleErrors; _errors = []; if (!_reportMultipleErrors) { try { - _validate(_rootSchema, instance); + _validate(_rootSchema, data); return true; } on FormatException { return false; } catch (e) { - _logger.shout("Unexpected Exception: $e"); + // _logger.shout('Unexpected Exception: $e'); TODO: re-add logger return false; } } - _validate(_rootSchema, instance); + _validate(_rootSchema, data); return _errors.length == 0; } - static bool _typeMatch(SchemaType type, dynamic instance) { + static bool _typeMatch(SchemaType type, JsonSchema schema, dynamic instance) { switch (type) { - case SchemaType.OBJECT: + case SchemaType.object: return instance is Map; - case SchemaType.STRING: + case SchemaType.string: return instance is String; - case SchemaType.INTEGER: - return instance is int; - case SchemaType.NUMBER: + case SchemaType.integer: + return instance is int || + (schema.schemaVersion == SchemaVersion.draft6 && instance is num && instance.remainder(1) == 0); + case SchemaType.number: return instance is num; - case SchemaType.ARRAY: + case SchemaType.array: return instance is List; - case SchemaType.BOOLEAN: + case SchemaType.boolean: return instance is bool; - case SchemaType.NULL: + case SchemaType.nullValue: return instance == null; } return false; } - void _numberValidation(Schema schema, num n) { - var maximum = schema._maximum; - var minimum = schema._minimum; - if (maximum != null) { - if (schema.exclusiveMaximum) { - if (n >= maximum) { - _err("${schema._path}: maximum exceeded ($n >= $maximum)"); - } - } else { - if (n > maximum) { - _err("${schema._path}: maximum exceeded ($n > $maximum)"); - } + void _numberValidation(JsonSchema schema, Instance instance) { + final num n = instance.data; + + final maximum = schema.maximum; + final minimum = schema.minimum; + final exclusiveMaximum = schema.exclusiveMaximum; + final exclusiveMinimum = schema.exclusiveMinimum; + + if (exclusiveMaximum != null) { + if (n >= exclusiveMaximum) { + _err('exclusiveMaximum exceeded ($n >= $exclusiveMaximum)', instance.path, schema.path); + } + } else if (maximum != null) { + if (n > maximum) { + _err('maximum exceeded ($n > $maximum)', instance.path, schema.path); + } + } + + if (exclusiveMinimum != null) { + if (n <= exclusiveMinimum) { + _err('exclusiveMinimum violated ($n <= $exclusiveMinimum)', instance.path, schema.path); } } else if (minimum != null) { - if (schema.exclusiveMinimum) { - if (n <= minimum) { - _err("${schema._path}: minimum violated ($n <= $minimum)"); - } - } else { - if (n < minimum) { - _err("${schema._path}: minimum violated ($n < $minimum)"); - } + if (n < minimum) { + _err('minimum violated ($n < $minimum)', instance.path, schema.path); } } - var multipleOf = schema._multipleOf; + final multipleOf = schema.multipleOf; if (multipleOf != null) { if (multipleOf is int && n is int) { if (0 != n % multipleOf) { - _err("${schema._path}: multipleOf violated ($n % $multipleOf)"); + _err('multipleOf violated ($n % $multipleOf)', instance.path, schema.path); } } else { - double result = n / multipleOf; + final double result = n / multipleOf; if (result.truncate() != result) { - _err("${schema._path}: multipleOf violated ($n % $multipleOf)"); + _err('multipleOf violated ($n % $multipleOf)', instance.path, schema.path); } } } } - void _typeValidation(Schema schema, dynamic instance) { - var typeList = schema._schemaTypeList; + void _typeValidation(JsonSchema schema, dynamic instance) { + final typeList = schema.typeList; if (typeList != null && typeList.length > 0) { - if (!typeList.any((type) => _typeMatch(type, instance))) { - _err("${schema._path}: type: wanted ${typeList} got $instance"); + if (!typeList.any((type) => _typeMatch(type, schema, instance.data))) { + _err('type: wanted ${typeList} got $instance', instance.path, schema.path); } } } - void _enumValidation(Schema schema, dynamic instance) { - var enumValues = schema._enumValues; + void _constValidation(JsonSchema schema, dynamic instance) { + if (schema.hasConst && !JsonSchemaUtils.jsonEqual(instance.data, schema.constValue)) { + _err('const violated ${instance}', instance.path, schema.path); + } + } + + void _enumValidation(JsonSchema schema, dynamic instance) { + final enumValues = schema.enumValues; if (enumValues.length > 0) { try { - enumValues.singleWhere((v) => _jsonEqual(instance, v)); + enumValues.singleWhere((v) => JsonSchemaUtils.jsonEqual(instance.data, v)); } on StateError { - _err("${schema._path}: enum violated ${instance}"); + _err('enum violated ${instance}', instance.path, schema.path); } } } - void _stringValidation(Schema schema, String instance) { - int actual = instance.length; - var minLength = schema._minLength; - var maxLength = schema._maxLength; + void _stringValidation(JsonSchema schema, Instance instance) { + final actual = instance.data.runes.length; + final minLength = schema.minLength; + final maxLength = schema.maxLength; if (maxLength is int && actual > maxLength) { - _err("${schema._path}: maxLength exceeded ($instance vs $maxLength)"); + _err('maxLength exceeded ($instance vs $maxLength)', instance.path, schema.path); } else if (minLength is int && actual < minLength) { - _err("${schema._path}: minLength violated ($instance vs $minLength)"); + _err('minLength violated ($instance vs $minLength)', instance.path, schema.path); } - var pattern = schema._pattern; - if (pattern != null && !pattern.hasMatch(instance)) { - _err("${schema._path}: pattern violated ($instance vs $pattern)"); + final pattern = schema.pattern; + if (pattern != null && !pattern.hasMatch(instance.data)) { + _err('pattern violated ($instance vs $pattern)', instance.path, schema.path); } } - void _itemsValidation(Schema schema, dynamic instance) { - int actual = instance.length; + void _itemsValidation(JsonSchema schema, Instance instance) { + final int actual = instance.data.length; - var singleSchema = schema._items; + final singleSchema = schema.items; if (singleSchema != null) { - instance.forEach((item) => _validate(singleSchema, item)); + instance.data.asMap().forEach((index, item) { + final itemInstance = Instance(item, path: '${instance.path}/$index'); + _validate(singleSchema, itemInstance); + }); } else { - var items = schema._itemsList; - var additionalItems = schema._additionalItems; + final items = schema.itemsList; if (items != null) { - int expected = items.length; - int end = min(expected, actual); + final expected = items.length; + final end = min(expected, actual); for (int i = 0; i < end; i++) { assert(items[i] != null); - _validate(items[i], instance[i]); + final itemInstance = Instance(instance.data[i], path: '${instance.path}/$i'); + _validate(items[i], itemInstance); } - if (additionalItems is Schema) { + if (schema.additionalItemsSchema != null) { for (int i = end; i < actual; i++) { - _validate(additionalItems, instance[i]); + final itemInstance = Instance(instance.data[i], path: '${instance.path}/$i'); + _validate(schema.additionalItemsSchema, itemInstance); } - } else if (additionalItems is bool) { - if (!additionalItems && actual > end) { - _err("${schema._path}: additionalItems false"); + } else if (schema.additionalItemsBool != null) { + if (!schema.additionalItemsBool && actual > end) { + _err('additionalItems false', instance.path, schema.path + '/additionalItems'); } } } } - var maxItems = schema._maxItems; - var minItems = schema._minItems; + final maxItems = schema.maxItems; + final minItems = schema.minItems; if (maxItems is int && actual > maxItems) { - _err("${schema._path}: maxItems exceeded ($actual vs $maxItems)"); - } else if (schema._minItems is int && actual < schema._minItems) { - _err("${schema._path}: minItems violated ($actual vs $minItems)"); + _err('maxItems exceeded ($actual vs $maxItems)', instance.path, schema.path); + } else if (schema.minItems is int && actual < schema.minItems) { + _err('minItems violated ($actual vs $minItems)', instance.path, schema.path); } - if (schema._uniqueItems) { - int end = instance.length; - int penultimate = end - 1; + if (schema.uniqueItems) { + final end = instance.data.length; + final penultimate = end - 1; for (int i = 0; i < penultimate; i++) { for (int j = i + 1; j < end; j++) { - if (_jsonEqual(instance[i], instance[j])) { - _err("${schema._path}: uniqueItems violated: $instance [$i]==[$j]"); + if (JsonSchemaUtils.jsonEqual(instance.data[i], instance.data[j])) { + _err('uniqueItems violated: $instance [$i]==[$j]', instance.path, schema.path); } } } } - } - void _validateAllOf(Schema schema, instance) { - List schemas = schema._allOf; - int errorsSoFar = _errors.length; - int i = 0; - schemas.every((s) { - assert(s != null); - _validate(s, instance); - bool valid = _errors.length == errorsSoFar; - if (!valid) { - _err("${s._path}/$i: allOf violated ${instance}"); + if (schema.contains != null) { + if (!instance.data.any((item) => Validator(schema.contains).validate(item))) { + _err('contains violated: $instance', instance.path, schema.path); } - i++; - return valid; - }); + } + } + + void _validateAllOf(JsonSchema schema, Instance instance) { + if (!schema.allOf.every((s) => Validator(s).validate(instance))) { + _err('${schema.path}: allOf violated ${instance}', instance.path, schema.path + '/allOf'); + } } - void _validateAnyOf(Schema schema, instance) { - if (!schema._anyOf.any((s) => new Validator(s).validate(instance))) { - _err( - "${schema._path}/anyOf: anyOf violated ($instance, ${schema._anyOf})"); + void _validateAnyOf(JsonSchema schema, Instance instance) { + if (!schema.anyOf.any((s) => Validator(s).validate(instance))) { + // TODO: deal with /anyOf + _err('${schema.path}/anyOf: anyOf violated ($instance, ${schema.anyOf})', instance.path, schema.path + '/anyOf'); } } - void _validateOneOf(Schema schema, instance) { + void _validateOneOf(JsonSchema schema, Instance instance) { try { - schema._oneOf.singleWhere((s) => new Validator(s).validate(instance)); + schema.oneOf.singleWhere((s) => Validator(s).validate(instance)); } on StateError catch (notOneOf) { - _err("${schema._path}/oneOf: violated ${notOneOf.message}"); + // TODO: deal with oneOf + _err('${schema.path}/oneOf: violated ${notOneOf.message}', instance.path, schema.path + '/oneOf'); } } - void _validateNot(Schema schema, instance) { - if (new Validator(schema._notSchema).validate(instance)) { - _err("${schema._notSchema._path}: not violated"); + void _validateNot(JsonSchema schema, Instance instance) { + if (Validator(schema.notSchema).validate(instance)) { + // TODO: deal with .notSchema + _err('${schema.notSchema.path}: not violated', instance.path, schema.notSchema.path); } } - void _validateFormat(Schema schema, instance) { - switch (schema._format) { + void _validateFormat(JsonSchema schema, Instance instance) { + // Non-strings in formats should be ignored. + if (instance.data is! String) return; + + switch (schema.format) { case 'date-time': { try { - DateTime.parse(instance); + DateTime.parse(instance.data); } catch (e) { - _err("'date-time' format not accepted $instance"); + _err('"date-time" format not accepted $instance', instance.path, schema.path); } } break; case 'uri': { - var isValid = - (_uriValidator != null) ? _uriValidator : _defaultUriValidator; + final isValid = defaultValidators.uriValidator ?? (_) => false; + + if (!isValid(instance.data)) { + _err('"uri" format not accepted $instance', instance.path, schema.path); + } + } + break; + case 'uri-reference': + { + if (schema.schemaVersion != SchemaVersion.draft6) + // TODO: deal with schema.format + _err('${schema.format} not supported as format before draft6', instance.path, schema.path); + final isValid = defaultValidators.uriReferenceValidator ?? (_) => false; + + if (!isValid(instance.data)) { + _err('"uri-reference" format not accepted $instance', instance.path, schema.path); + } + } + break; + case 'uri-template': + { + if (schema.schemaVersion != SchemaVersion.draft6) + _err('${schema.format} not supported as format before draft6', instance.path, schema.path); + final isValid = defaultValidators.uriTemplateValidator ?? (_) => false; - if (!isValid(instance)) { - _err("'uri' format not accepted $instance"); + if (!isValid(instance.data)) { + _err('"uri-template" format not accepted $instance', instance.path, schema.path); } } break; case 'email': { - var isValid = (_emailValidator != null) - ? _emailValidator - : _defaultEmailValidator; + final isValid = defaultValidators.emailValidator ?? (_) => false; - if (!isValid(instance)) { - _err("'email' format not accepted $instance"); + if (!isValid(instance.data)) { + _err('"email" format not accepted $instance', instance.path, schema.path); } } break; case 'ipv4': { - if (_ipv4Re.firstMatch(instance) == null) { - _err("'ipv4' format not accepted $instance"); + if (JsonSchemaValidationRegexes.ipv4.firstMatch(instance.data) == null) { + _err('"ipv4" format not accepted $instance', instance.path, schema.path); } } break; case 'ipv6': { - if (_ipv6Re.firstMatch(instance) == null) { - _err("'ipv6' format not accepted $instance"); + if (JsonSchemaValidationRegexes.ipv6.firstMatch(instance.data) == null) { + _err('ipv6" format not accepted $instance', instance.path, schema.path); } } break; case 'hostname': { - if (_hostnameRe.firstMatch(instance) == null) { - _err("'hostname' format not accepted $instance"); + if (JsonSchemaValidationRegexes.hostname.firstMatch(instance.data) == null) { + _err('"hostname" format not accepted $instance', instance.path, schema.path); + } + } + break; + case 'json-pointer': + { + if (schema.schemaVersion != SchemaVersion.draft6) + _err('${schema.format} not supported as format before draft6', instance.path, schema.path); + if (JsonSchemaValidationRegexes.jsonPointer.firstMatch(instance.data) == null) { + _err('json-pointer" format not accepted $instance', instance.path, schema.path); } } break; default: { - _err("${schema._format} not supported as format"); + _err('${schema.format} not supported as format', instance.path, schema.path); } } } - void _objectPropertyValidation(Schema schema, Map instance) { - bool propMustValidate = - schema._additionalProperties != null && !schema._additionalProperties; + void _objectPropertyValidation(JsonSchema schema, Instance instance) { + final propMustValidate = schema.additionalPropertiesBool != null && !schema.additionalPropertiesBool; + + instance.data.forEach((k, v) { + // Validate property names against the provided schema, if any. + if (schema.propertyNamesSchema != null) { + _validate(schema.propertyNamesSchema, k); + } + + final newInstance = Instance(v, path: '${instance.path}/$k'); - instance.forEach((k, v) { bool propCovered = false; - Schema propSchema = schema._properties[k]; + final JsonSchema propSchema = schema.properties[k]; if (propSchema != null) { assert(propSchema != null); - _validate(propSchema, v); + _validate(propSchema, newInstance); propCovered = true; } - schema._patternProperties.forEach((regex, patternSchema) { + schema.patternProperties.forEach((regex, patternSchema) { if (regex.hasMatch(k)) { assert(patternSchema != null); - _validate(patternSchema, v); + _validate(patternSchema, newInstance); propCovered = true; } }); if (!propCovered) { - if (schema._additionalPropertiesSchema != null) { - _validate(schema._additionalPropertiesSchema, v); + if (schema.additionalPropertiesSchema != null) { + _validate(schema.additionalPropertiesSchema, newInstance); } else if (propMustValidate) { - _err("${schema._path}: unallowed additional property $k"); + _err('unallowed additional property $k', instance.path, schema.path + '/additionalProperties'); } } }); } - void _propertyDependenciesValidation(Schema schema, Map instance) { - schema._propertyDependencies.forEach((k, dependencies) { - if (instance.containsKey(k)) { - if (!dependencies.every((prop) => instance.containsKey(prop))) { - _err("${schema._path}: prop $k => $dependencies required"); + void _propertyDependenciesValidation(JsonSchema schema, Instance instance) { + schema.propertyDependencies.forEach((k, dependencies) { + if (instance.data.containsKey(k)) { + if (!dependencies.every((prop) => instance.data.containsKey(prop))) { + _err('prop $k => $dependencies required', instance.path, schema.path + '/dependencies'); } } }); } - void _schemaDependenciesValidation(Schema schema, Map instance) { - schema._schemaDependencies.forEach((k, otherSchema) { - if (instance.containsKey(k)) { - if (!new Validator(otherSchema).validate(instance)) { - _err("${otherSchema._path}: prop $k violated schema dependency"); + void _schemaDependenciesValidation(JsonSchema schema, Instance instance) { + schema.schemaDependencies.forEach((k, otherSchema) { + if (instance.data.containsKey(k)) { + if (!Validator(otherSchema).validate(instance)) { + _err('prop $k violated schema dependency', instance.path, otherSchema.path); } } }); } - void _objectValidation(Schema schema, Map instance) { - int numProps = instance.length; - int minProps = schema._minProperties; - int maxProps = schema._maxProperties; + void _objectValidation(JsonSchema schema, Instance instance) { + // Min / Max Props + final numProps = instance.data.length; + final minProps = schema.minProperties; + final maxProps = schema.maxProperties; if (numProps < minProps) { - _err( - "${schema._path}: minProperties violated (${numProps} < ${minProps})"); + _err('minProperties violated (${numProps} < ${minProps})', instance.path, schema.path); } else if (maxProps != null && numProps > maxProps) { - _err( - "${schema._path}: maxProperties violated (${numProps} > ${maxProps})"); + _err('maxProperties violated (${numProps} > ${maxProps})', instance.path, schema.path); } - if (schema._requiredProperties != null) { - schema._requiredProperties.forEach((prop) { - if (!instance.containsKey(prop)) { - _err( - "${schema._path}: required prop missing: ${prop} from $instance"); + + // Required Properties + if (schema.requiredProperties != null) { + schema.requiredProperties.forEach((prop) { + if (!instance.data.containsKey(prop)) { + _err('required prop missing: ${prop} from $instance', instance.path, schema.path + '/required'); } }); } + _objectPropertyValidation(schema, instance); - if (schema._propertyDependencies != null) - _propertyDependenciesValidation(schema, instance); + if (schema.propertyDependencies != null) _propertyDependenciesValidation(schema, instance); - if (schema._schemaDependencies != null) - _schemaDependenciesValidation(schema, instance); + if (schema.schemaDependencies != null) _schemaDependenciesValidation(schema, instance); } - void _validate(Schema schema, dynamic instance) { + void _validate(JsonSchema schema, dynamic instance) { + if (instance is! Instance) { + instance = Instance(instance); + } + + /// If the [JsonSchema] being validated is a ref, pull the ref + /// from the [refMap] instead. + while (schema.ref != null) { + schema = schema.resolvePath(schema.ref); + } + + /// If the [JsonSchema] is a bool, always return this value. + if (schema.schemaBool != null) { + if (schema.schemaBool == false) { + _err('schema is a boolean == false, this schema will never validate. Instance: $instance', instance.path, + schema.path); + } + return; + } + _typeValidation(schema, instance); + _constValidation(schema, instance); _enumValidation(schema, instance); - if (instance is List) _itemsValidation(schema, instance); - if (instance is String) _stringValidation(schema, instance); - if (instance is num) _numberValidation(schema, instance); - if (schema._allOf.length > 0) _validateAllOf(schema, instance); - if (schema._anyOf.length > 0) _validateAnyOf(schema, instance); - if (schema._oneOf.length > 0) _validateOneOf(schema, instance); - if (schema._notSchema != null) _validateNot(schema, instance); - if (schema._format != null) _validateFormat(schema, instance); - if (instance is Map) _objectValidation(schema, instance); + if (instance.data is List) _itemsValidation(schema, instance); + if (instance.data is String) _stringValidation(schema, instance); + if (instance.data is num) _numberValidation(schema, instance); + if (schema.allOf.length > 0) _validateAllOf(schema, instance); + if (schema.anyOf.length > 0) _validateAnyOf(schema, instance); + if (schema.oneOf.length > 0) _validateOneOf(schema, instance); + if (schema.notSchema != null) _validateNot(schema, instance); + if (schema.format != null) _validateFormat(schema, instance); + if (instance.data is Map) _objectValidation(schema, instance); } - void _err(String msg) { - _logger.warning(msg); - _errors.add(msg); - if (!_reportMultipleErrors) throw new FormatException(msg); - } + void _err(String msg, String instancePath, String schemaPath) { + // _logger.warning(msg); TODO: re-add logger - // end + schemaPath = schemaPath.replaceFirst('#', ''); + _errors.add(ValidationError._(instancePath, schemaPath, msg)); + if (!_reportMultipleErrors) throw FormatException(msg); + } - Schema _rootSchema; - List _errors = []; + JsonSchema _rootSchema; + List _errors = []; bool _reportMultipleErrors; } - -// custom -// end - -RegExp _emailRe = new RegExp(r'^[_A-Za-z0-9-\+]+(\.[_A-Za-z0-9-]+)*' - r'@' - r'[A-Za-z0-9-]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})$'); -var _defaultEmailValidator = - (String email) => _emailRe.firstMatch(email) != null; -var _emailValidator = _defaultEmailValidator; -RegExp _ipv4Re = new RegExp(r'^(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))\.' - r'(\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))$'); -RegExp _ipv6Re = new RegExp(r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6}$)|' - r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}$)|' - r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}$)|' - r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}$)|' - r'(^([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}$)|' - r'(^([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1}$)|' - r'(^(([0-9a-f]{1,4}:){1,7}|:):$)|' - r'(^:(:[0-9a-f]{1,4}){1,7}$)|' - r'(^((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' - r'(^(([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3})$)|' - r'(^([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^(([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)|' - r'(^:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$)'); -var _defaultUriValidator = _defaultUriValidatorImpl; -bool _defaultUriValidatorImpl(String uri) { - try { - final result = Uri.parse(uri); - if (result.path.startsWith('//')) return false; - return true; - } catch (e) { - return false; - } -} -RegExp _hostnameRe = new RegExp(r'^(?=.{1,255}$)' - r'[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?' - r'(?:\.[0-9A-Za-z](?:(?:[0-9A-Za-z]|-){0,61}[0-9A-Za-z])?)*\.?$'); -var _uriValidator = _defaultUriValidator; diff --git a/lib/src/json_schema/vm/platform_functions.dart b/lib/src/json_schema/vm/platform_functions.dart new file mode 100644 index 00000000..ab2feb45 --- /dev/null +++ b/lib/src/json_schema/vm/platform_functions.dart @@ -0,0 +1,74 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import 'dart:convert'; +import 'dart:io'; +import 'dart:async'; +import 'dart:convert' as convert; + +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:json_schema/src/json_schema/json_schema.dart'; +import 'package:json_schema/src/json_schema/utils.dart'; + +Future createSchemaFromUrlVm(String schemaUrl, {SchemaVersion schemaVersion}) async { + final uriWithFrag = Uri.parse(schemaUrl); + final uri = schemaUrl.endsWith('#') ? uriWithFrag : uriWithFrag.removeFragment(); + Map schemaMap; + if (uri.scheme == 'http' || uri.scheme == 'https') { + // Setup the HTTP request. + final httpRequest = await HttpClient().getUrl(uri); + httpRequest.followRedirects = true; + // Fetch the response + final response = await httpRequest.close(); + // Convert the response into a string + if (response.statusCode == HttpStatus.notFound) { + throw ArgumentError('Schema at URL: $schemaUrl can\'t be found.'); + } + final schemaText = await convert.Utf8Decoder().bind(response).join(); + schemaMap = json.decode(schemaText); + } else if (uri.scheme == 'file' || uri.scheme == '') { + final fileString = await File(uri.scheme == 'file' ? uri.toFilePath() : schemaUrl).readAsString(); + schemaMap = json.decode(fileString); + } else { + throw FormatException('Url schema must be http, file, or empty: $schemaUrl'); + } + // HTTP servers / file systems ignore fragments, so resolve a sub-map if a fragment was specified. + final parentSchema = await JsonSchema.createSchemaAsync(schemaMap, schemaVersion: schemaVersion, fetchedFromUri: uri); + final schema = JsonSchemaUtils.getSubMapFromFragment(parentSchema, uriWithFrag); + return schema ?? parentSchema; +} diff --git a/lib/vm.dart b/lib/vm.dart new file mode 100644 index 00000000..eb5237ea --- /dev/null +++ b/lib/vm.dart @@ -0,0 +1,49 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +library json_schema.vm; + +import 'package:json_schema/src/json_schema/global_platform_functions.dart'; +import 'package:json_schema/src/json_schema/vm/platform_functions.dart' show createSchemaFromUrlVm; + +export 'package:json_schema/src/json_schema/vm/platform_functions.dart' show createSchemaFromUrlVm; + +/// Configures json_schema for use in the browser via dart:html. +void configureJsonSchemaForVm() { + globalCreateJsonSchemaFromUrl = createSchemaFromUrlVm; +} diff --git a/pubspec.yaml b/pubspec.yaml index 277557b6..736ecf6e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,39 +1,32 @@ name: json_schema -version: 1.0.7 -author: Daniel Davidson -homepage: https://github.com/patefacio/json_schema -description: > - Provide support for validating instances against json schema +version: 2.2.1 +description: Provide support for validating instances against json schema +authors: + - Michael Carter + - Daniel Davidson +homepage: https://github.com/workiva/json_schema + environment: - sdk: '>=1.8.2 <2.0.0' + sdk: '>=2.4.0 <3.0.0' dependencies: - -# custom - - path: "^1.3.0" - logging: ">=0.9.3<0.12.0" - args: ">=0.11.0 <2.0.0" - -# end + args: ">=0.13.7 <2.0.0" + collection: ^1.14.6 + json_pointer: ^0.1.0 + logging: ">=0.9.3 <0.12.0" + path: ^1.3.0 + uri: ">=0.11.1 <0.12.0" + w_transport: ^3.2.8 dev_dependencies: - -# custom - - test: "^0.12.13" - yaml: "^2.1.0" - coverage: ">=0.7.6" - -# end - -dependency_overrides: -# custom - -# end - -transformers: - -# custom -# end - + build_runner: ^1.7.2 + build_test: ^0.10.10 + build_web_compilers: ^2.9.0 + build_vm_compilers: ^1.0.4 + dart_dev: ^3.6.1 + dart_style: ^1.3.1 + dependency_validator: ^1.4.0 + glob: ^1.1.7 + shelf: ^0.7.2 + shelf_static: ^0.2.7 + test: ^1.9.1 diff --git a/test/JSON-Schema-Test-Suite/.github/workflows/ci.yml b/test/JSON-Schema-Test-Suite/.github/workflows/ci.yml new file mode 100755 index 00000000..9fdb5083 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: Test Suite Sanity Checking + +on: + push: + pull_request: + release: + types: [published] + schedule: + # Daily at 6:42 + - cron: '42 6 * * *' + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install tox + run: python -m pip install tox + - name: Run the sanity checks + run: python -m tox diff --git a/test/JSON-Schema-Test-Suite/.gitignore b/test/JSON-Schema-Test-Suite/.gitignore old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/.travis.yml b/test/JSON-Schema-Test-Suite/.travis.yml deleted file mode 100644 index deecd611..00000000 --- a/test/JSON-Schema-Test-Suite/.travis.yml +++ /dev/null @@ -1,4 +0,0 @@ -language: python -python: "2.7" -install: pip install jsonschema -script: bin/jsonschema_suite check diff --git a/test/JSON-Schema-Test-Suite/LICENSE b/test/JSON-Schema-Test-Suite/LICENSE old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/README.md b/test/JSON-Schema-Test-Suite/README.md old mode 100644 new mode 100755 index 1ad3eb2a..cfea40f6 --- a/test/JSON-Schema-Test-Suite/README.md +++ b/test/JSON-Schema-Test-Suite/README.md @@ -1,5 +1,4 @@ -JSON Schema Test Suite [![Build Status](https://travis-ci.org/json-schema/JSON-Schema-Test-Suite.png?branch=develop)](https://travis-ci.org/json-schema/JSON-Schema-Test-Suite) -====================== +# JSON Schema Test Suite [![Build Status](https://github.com/json-schema-org/JSON-Schema-Test-Suite/workflows/Test%20Suite%20Sanity%20Checking/badge.svg)](https://github.com/json-schema-org/JSON-Schema-Test-Suite/actions?query=workflow%3A%22Test+Suite+Sanity+Checking%22) This repository contains a set of JSON objects that implementors of JSON Schema validation libraries can use to test their validators. @@ -9,72 +8,197 @@ It is meant to be language agnostic and should require only a JSON parser. The conversion of the JSON objects into tests within your test framework of choice is still the job of the validator implementor. -Structure of a Test -------------------- +## Structure of a Test -If you're going to use this suite, you need to know how tests are laid out. The -tests are contained in the `tests` directory at the root of this repository. +The tests in this suite are contained in the `tests` directory at the root of +this repository. Inside that directory is a subdirectory for each draft or +version of the specification. -Inside that directory is a subdirectory for each draft or version of the -schema. We'll use `draft3` as an example. +Inside each draft directory, there are a number of `.json` files and one or more +special subdirectories. The subdirectories contain `.json` files meant for a +specific testing purpose, and each `.json` file logically groups a set of test +cases together. Often the grouping is by property under test, but not always. -If you look inside the draft directory, there are a number of `.json` files, -which logically group a set of test cases together. Often the grouping is by -property under test, but not always, especially within optional test files -(discussed below). +The subdirectories are described in the next section. Inside each `.json` file is a single array containing objects. It's easiest to illustrate the structure of these with an example: ```json - { - "description": "the description of the test case", - "schema": {"the schema that should" : "be validated against"}, - "tests": [ - { - "description": "a specific test of a valid instance", - "data": "the instance", - "valid": true - }, - { - "description": "another specific test this time, invalid", - "data": 15, - "valid": false - } - ] - } +{ + "description": "The description of the test case", + "schema": { + "description": "The schema against which the data in each test is validated", + "type": "string" + }, + "tests": [ + { + "description": "Test for a valid instance", + "data": "the instance to validate", + "valid": true + }, + { + "description": "Test for an invalid instance", + "data": 15, + "valid": false + } + ] +} ``` -So a description, a schema, and some tests, where tests is an array containing -one or more objects with descriptions, data, and a boolean indicating whether -they should be valid or invalid. +In short: a description, a schema under test, and some tests, where each test +in the `tests` array is an objects with a description of the case itself, the +instance under test, and a boolean indicating whether it should be valid +or invalid. -Coverage --------- +## Test Subdirectories -Draft 3 and 4 should have full coverage. If you see anything missing or think -there is a useful test missing, please send a pull request or open an issue. +There is currently only one subdirectory that may exist within each draft +directory. This is: -Who Uses the Test Suite ------------------------ +1. `optional/`: Contains tests that are considered optional. + +## Coverage + +Drafts 07, 06, 04, and 03 should have full coverage, with tests for drafts 06, +07, and 2019-09 being considered current and actively supported. Draft 2019-09 +is almost fully covered. + +Contributions are very welcome, especially from implementers as they add support +to their own implementations. + +If you see anything missing from the current supported drafts, or incorrect on +any draft still accepting bug fixes, please +[file an issue](https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues) +or [submit a PR](https://github.com/json-schema-org/JSON-Schema-Test-Suite). + +## Who Uses the Test Suite This suite is being used by: - * [json-schema-validator (Java)](https://github.com/fge/json-schema-validator) - * [jsonschema (python)](https://github.com/Julian/jsonschema) - * [aeson-schema (haskell)](https://github.com/timjb/aeson-schema) - * [direct-schema (javascript)](https://github.com/IreneKnapp/direct-schema) - * [jsonschema (javascript)](https://github.com/tdegrunt/jsonschema) - * [JaySchema (javascript)](https://github.com/natesilva/jayschema) - * [z-schema (javascript)](https://github.com/zaggino/z-schema) - * [jesse (Erlang)](https://github.com/klarna/jesse) - * [json-schema (PHP)](https://github.com/justinrainbow/json-schema) - * [gojsonschema (Go)](https://github.com/sigu-399/gojsonschema) +### Clojure + +* [jinx](https://github.com/juxt/jinx) +* [json-schema](https://github.com/tatut/json-schema) + +### Coffeescript + +* [jsck](https://github.com/pandastrike/jsck) + +### Common Lisp + +* [json-schema](https://github.com/fisxoj/json-schema) + +### C++ + +* [Modern C++ JSON schema validator](https://github.com/pboettch/json-schema-validator) + +### Dart + +* [json_schema](https://github.com/patefacio/json_schema) + +### Elixir + +* [ex_json_schema](https://github.com/jonasschmidt/ex_json_schema) + +### Erlang + +* [jesse](https://github.com/for-GET/jesse) + +### Go + +* [gojsonschema](https://github.com/sigu-399/gojsonschema) +* [validate-json](https://github.com/cesanta/validate-json) + +### Haskell + +* [aeson-schema](https://github.com/timjb/aeson-schema) +* [hjsonschema](https://github.com/seagreen/hjsonschema) + +### Java + +* [json-schema-validator](https://github.com/daveclayton/json-schema-validator) +* [everit-org/json-schema](https://github.com/everit-org/json-schema) +* [networknt/json-schema-validator](https://github.com/networknt/json-schema-validator) +* [Justify](https://github.com/leadpony/justify) +* [Snow](https://github.com/ssilverman/snowy-json) +* [jsonschemafriend](https://github.com/jimblackler/jsonschemafriend) + +### JavaScript + +* [json-schema-benchmark](https://github.com/Muscula/json-schema-benchmark) +* [direct-schema](https://github.com/IreneKnapp/direct-schema) +* [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid) +* [jassi](https://github.com/iclanzan/jassi) +* [JaySchema](https://github.com/natesilva/jayschema) +* [json-schema-valid](https://github.com/ericgj/json-schema-valid) +* [Jsonary](https://github.com/jsonary-js/jsonary) +* [jsonschema](https://github.com/tdegrunt/jsonschema) +* [request-validator](https://github.com/bugventure/request-validator) +* [skeemas](https://github.com/Prestaul/skeemas) +* [tv4](https://github.com/geraintluff/tv4) +* [z-schema](https://github.com/zaggino/z-schema) +* [jsen](https://github.com/bugventure/jsen) +* [ajv](https://github.com/epoberezkin/ajv) +* [djv](https://github.com/korzio/djv) + +### Node.js + +For node.js developers, the suite is also available as an +[npm](https://www.npmjs.com/package/@json-schema-org/tests) package. + +Node-specific support is maintained in a [separate +repository](https://github.com/json-schema-org/json-schema-test-suite-npm) +which also welcomes your contributions! + +### .NET + +* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema) +* [Manatee.Json](https://github.com/gregsdennis/Manatee.Json) + +### Perl + +* [JSON::Schema::Draft201909](https://github.com/karenetheridge/JSON-Schema-Draft201909) +* [Test::JSON::Schema::Acceptance](https://github.com/karenetheridge/Test-JSON-Schema-Acceptance) + +### PHP + +* [json-schema](https://github.com/justinrainbow/json-schema) +* [json-guard](https://github.com/thephpleague/json-guard) + +### PostgreSQL + +* [postgres-json-schema](https://github.com/gavinwahl/postgres-json-schema) +* [is_jsonb_valid](https://github.com/furstenheim/is_jsonb_valid) + +### Python + +* [jsonschema](https://github.com/Julian/jsonschema) +* [fastjsonschema](https://github.com/seznam/python-fastjsonschema) +* [hypothesis-jsonschema](https://github.com/Zac-HD/hypothesis-jsonschema) + +### Ruby + +* [json-schema](https://github.com/hoxworth/json-schema) +* [json_schemer](https://github.com/davishmcclurg/json_schemer) + +### Rust + +* [jsonschema](https://github.com/Stranger6667/jsonschema-rs) +* [valico](https://github.com/rustless/valico) + +### Swift + +* [JSONSchema](https://github.com/kylef/JSONSchema.swift) If you use it as well, please fork and send a pull request adding yourself to the list :). -Contributing ------------- +## Contributing If you see something missing or incorrect, a pull request is most welcome! + +There are some sanity checks in place for testing the test suite. You can run +them with `bin/jsonschema_suite check` or `tox`. They will be run automatically +by [GitHub Actions](https://github.com/json-schema-org/JSON-Schema-Test-Suite/actions?query=workflow%3A%22Test+Suite+Sanity+Checking%22) +as well. diff --git a/test/JSON-Schema-Test-Suite/bin/jsonschema_suite b/test/JSON-Schema-Test-Suite/bin/jsonschema_suite index 96108c86..5f5d133f 100755 --- a/test/JSON-Schema-Test-Suite/bin/jsonschema_suite +++ b/test/JSON-Schema-Test-Suite/bin/jsonschema_suite @@ -1,27 +1,15 @@ -#! /usr/bin/env python +#! /usr/bin/env python3 from __future__ import print_function -import sys -import textwrap - -try: - import argparse -except ImportError: - print(textwrap.dedent(""" - The argparse library could not be imported. jsonschema_suite requires - either Python 2.7 or for you to install argparse. You can do so by - running `pip install argparse`, `easy_install argparse` or by - downloading argparse and running `python2.6 setup.py install`. - - See https://pypi.python.org/pypi/argparse for details. - """.strip("\n"))) - sys.exit(1) - +from pprint import pformat +import argparse import errno import fnmatch import json import os import random import shutil +import sys +import textwrap import unittest import warnings @@ -29,56 +17,48 @@ if getattr(unittest, "skipIf", None) is None: unittest.skipIf = lambda cond, msg : lambda fn : fn try: - import jsonschema + import jsonschema.validators except ImportError: jsonschema = None -else: - validators = getattr( - jsonschema.validators, "validators", jsonschema.validators - ) -ROOT_DIR = os.path.join( - os.path.dirname(__file__), os.pardir).rstrip("__pycache__") +ROOT_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__), os.pardir).rstrip("__pycache__"), +) SUITE_ROOT_DIR = os.path.join(ROOT_DIR, "tests") REMOTES = { - "integer.json": {"type": "integer"}, + "integer.json": {u"type": u"integer"}, + "name.json": { + u"type": "string", + u"definitions": { + u"orNull": {u"anyOf": [{u"type": u"null"}, {u"$ref": u"#"}]}, + }, + }, + "name-defs.json": { + u"type": "string", + u"$defs": { + u"orNull": {u"anyOf": [{u"type": u"null"}, {u"$ref": u"#"}]}, + }, + }, "subSchemas.json": { - "integer": {"type": "integer"}, - "refToInteger": {"$ref": "#/integer"}, + u"integer": {u"type": u"integer"}, + u"refToInteger": {u"$ref": u"#/integer"}, + }, + "subSchemas-defs.json": { + u"$defs": { + u"integer": {u"type": u"integer"}, + u"refToInteger": {u"$ref": u"#/$defs/integer"}, + } }, - "folder/folderInteger.json": {"type": "integer"} + "baseUriChange/folderInteger.json": {u"type": u"integer"}, + "baseUriChangeFolder/folderInteger.json": {u"type": u"integer"}, + "baseUriChangeFolderInSubschema/folderInteger.json": {u"type": u"integer"}, } REMOTES_DIR = os.path.join(ROOT_DIR, "remotes") -TESTSUITE_SCHEMA = { - "$schema": "http://json-schema.org/draft-03/schema#", - "type": "array", - "items": { - "type": "object", - "properties": { - "description": {"type": "string", "required": True}, - "schema": {"required": True}, - "tests": { - "type": "array", - "items": { - "type": "object", - "properties": { - "description": {"type": "string", "required": True}, - "data": {"required": True}, - "valid": {"type": "boolean", "required": True} - }, - "additionalProperties": False - }, - "minItems": 1 - } - }, - "additionalProperties": False, - "minItems": 1 - } -} - +with open(os.path.join(ROOT_DIR, "test-schema.json")) as schema: + TESTSUITE_SCHEMA = json.load(schema) def files(paths): for path in paths: @@ -123,11 +103,13 @@ class SanityTests(unittest.TestCase): def test_all_descriptions_have_reasonable_length(self): for case in cases(self.test_files): - descript = case["description"] + description = case["description"] self.assertLess( - len(descript), - 60, - "%r is too long! (keep it to less than 60 chars)" % (descript,) + len(description), + 70, + "%r is too long! (keep it to less than 70 chars)" % ( + description, + ), ) def test_all_descriptions_are_unique(self): @@ -142,7 +124,7 @@ class SanityTests(unittest.TestCase): @unittest.skipIf(jsonschema is None, "Validation library not present!") def test_all_schemas_are_valid(self): for schema in os.listdir(SUITE_ROOT_DIR): - schema_validator = validators.get(schema) + schema_validator = jsonschema.validators.validators.get(schema) if schema_validator is not None: test_files = collect(os.path.join(SUITE_ROOT_DIR, schema)) for case in cases(test_files): @@ -156,7 +138,8 @@ class SanityTests(unittest.TestCase): @unittest.skipIf(jsonschema is None, "Validation library not present!") def test_suites_are_valid(self): - validator = jsonschema.Draft3Validator(TESTSUITE_SCHEMA) + Validator = jsonschema.validators.validator_for(TESTSUITE_SCHEMA) + validator = Validator(TESTSUITE_SCHEMA) for tests in files(self.test_files): try: validator.validate(tests) @@ -164,10 +147,46 @@ class SanityTests(unittest.TestCase): self.fail(str(error)) def test_remote_schemas_are_updated(self): - for url, schema in REMOTES.items(): - filepath = os.path.join(REMOTES_DIR, url) - with open(filepath) as schema_file: - self.assertEqual(json.load(schema_file), schema) + files = {} + for parent, _, paths in os.walk(REMOTES_DIR): + for path in paths: + absolute_path = os.path.join(parent, path) + with open(absolute_path) as schema_file: + files[absolute_path] = json.load(schema_file) + + expected = { + os.path.join(REMOTES_DIR, path): contents + for path, contents in REMOTES.items() + } + + missing = set(files).symmetric_difference(expected) + changed = { + path + for path, contents in expected.items() + if path in files + and contents != files[path] + } + + self.assertEqual( + files, + expected, + msg=textwrap.dedent( + """ + Remotes in the remotes/ directory do not match those in the + ``jsonschema_suite`` Python script. + + Unfortunately for the minute, each remote file is duplicated in + two places.""" + (""" + + Only present in one location: + + {}""".format("\n".join(missing)) if missing else "") + (""" + + Conflicting between the two: + + {}""".format("\n".join(changed)) if changed else "") + ) + ) def main(arguments): @@ -205,8 +224,9 @@ def main(arguments): if e.errno != errno.EEXIST: raise - with open(filepath, "wb") as out_file: + with open(filepath, "w") as out_file: json.dump(schema, out_file, indent=4, sort_keys=True) + out_file.write("\n") elif arguments.command == "serve": try: from flask import Flask, jsonify diff --git a/test/JSON-Schema-Test-Suite/index.js b/test/JSON-Schema-Test-Suite/index.js new file mode 100755 index 00000000..7d010933 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/index.js @@ -0,0 +1,46 @@ +'use strict'; + +const Ajv = require('ajv'); +const jsonSchemaTest = require('json-schema-test'); + +const refs = { + 'http://localhost:1234/integer.json': require('./remotes/integer.json'), + 'http://localhost:1234/subSchemas.json': require('./remotes/subSchemas.json'), + 'http://localhost:1234/baseUriChange/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'), + 'http://localhost:1234/baseUriChangeFolder/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'), + 'http://localhost:1234/baseUriChangeFolderInSubschema/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'), + 'http://localhost:1234/name.json': require('./remotes/name.json'), + 'http://localhost:1234/name-defs.json': require('./remotes/name-defs.json') +}; + +const SKIP = { + 4: ['optional/zeroTerminatedFloats'], + 7: [ + 'format/idn-email', + 'format/idn-hostname', + 'format/iri', + 'format/iri-reference', + 'optional/content' + ] +}; + +[4, 6, 7].forEach((draft) => { + let ajv; + if (draft == 7) { + ajv = new Ajv({format: 'full'}); + } else { + const schemaId = draft == 4 ? 'id' : '$id'; + ajv = new Ajv({format: 'full', meta: false, schemaId}); + ajv.addMetaSchema(require(`ajv/lib/refs/json-schema-draft-0${draft}.json`)); + ajv._opts.defaultMeta = `http://json-schema.org/draft-0${draft}/schema#`; + } + for (const uri in refs) ajv.addSchema(refs[uri], uri); + + jsonSchemaTest(ajv, { + description: `Test suite draft-0${draft}`, + suites: {tests: `./tests/draft${draft}/{**/,}*.json`}, + skip: SKIP[draft], + cwd: __dirname, + hideFolder: 'tests/' + }); +}); diff --git a/test/JSON-Schema-Test-Suite/package.json b/test/JSON-Schema-Test-Suite/package.json new file mode 100755 index 00000000..3980136c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/package.json @@ -0,0 +1,28 @@ +{ + "name": "json-schema-test-suite", + "version": "0.1.0", + "description": "A language agnostic test suite for the JSON Schema specifications", + "main": "index.js", + "scripts": { + "test": "mocha index.js -R spec" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite.git" + }, + "keywords": [ + "json-schema", + "tests" + ], + "author": "http://json-schema.org", + "license": "MIT", + "bugs": { + "url": "https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues" + }, + "homepage": "https://github.com/json-schema-org/JSON-Schema-Test-Suite#readme", + "devDependencies": { + "ajv": "^6.0.0-rc.1", + "json-schema-test": "^2.0.0", + "mocha": "^3.2.0" + } +} diff --git a/test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json b/test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json old mode 100644 new mode 100755 similarity index 92% rename from test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json rename to test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json index dbe5c758..8b50ea30 --- a/test/JSON-Schema-Test-Suite/remotes/folder/folderInteger.json +++ b/test/JSON-Schema-Test-Suite/remotes/baseUriChange/folderInteger.json @@ -1,3 +1,3 @@ { "type": "integer" -} \ No newline at end of file +} diff --git a/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json b/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json new file mode 100755 index 00000000..8b50ea30 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolder/folderInteger.json @@ -0,0 +1,3 @@ +{ + "type": "integer" +} diff --git a/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json b/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json new file mode 100755 index 00000000..8b50ea30 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/remotes/baseUriChangeFolderInSubschema/folderInteger.json @@ -0,0 +1,3 @@ +{ + "type": "integer" +} diff --git a/test/JSON-Schema-Test-Suite/remotes/integer.json b/test/JSON-Schema-Test-Suite/remotes/integer.json old mode 100644 new mode 100755 index dbe5c758..8b50ea30 --- a/test/JSON-Schema-Test-Suite/remotes/integer.json +++ b/test/JSON-Schema-Test-Suite/remotes/integer.json @@ -1,3 +1,3 @@ { "type": "integer" -} \ No newline at end of file +} diff --git a/test/JSON-Schema-Test-Suite/remotes/name-defs.json b/test/JSON-Schema-Test-Suite/remotes/name-defs.json new file mode 100755 index 00000000..1dab4a43 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/remotes/name-defs.json @@ -0,0 +1,15 @@ +{ + "$defs": { + "orNull": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#" + } + ] + } + }, + "type": "string" +} diff --git a/test/JSON-Schema-Test-Suite/remotes/name.json b/test/JSON-Schema-Test-Suite/remotes/name.json new file mode 100755 index 00000000..fceacb80 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/remotes/name.json @@ -0,0 +1,15 @@ +{ + "definitions": { + "orNull": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#" + } + ] + } + }, + "type": "string" +} diff --git a/test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json b/test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json new file mode 100755 index 00000000..50b7b6dc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/remotes/subSchemas-defs.json @@ -0,0 +1,10 @@ +{ + "$defs": { + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/$defs/integer" + } + } +} diff --git a/test/JSON-Schema-Test-Suite/remotes/subSchemas.json b/test/JSON-Schema-Test-Suite/remotes/subSchemas.json old mode 100644 new mode 100755 index 8b6d8f84..9f8030bc --- a/test/JSON-Schema-Test-Suite/remotes/subSchemas.json +++ b/test/JSON-Schema-Test-Suite/remotes/subSchemas.json @@ -1,8 +1,8 @@ { "integer": { "type": "integer" - }, + }, "refToInteger": { "$ref": "#/integer" } -} \ No newline at end of file +} diff --git a/test/JSON-Schema-Test-Suite/test-schema.json b/test/JSON-Schema-Test-Suite/test-schema.json new file mode 100755 index 00000000..11b1e8d8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/test-schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "description": "Schema for tests", + "type": "array", + "items": { + "type": "object", + "required": [ "description", "schema", "tests" ], + "properties": { + "id": { + "description": "Uniquely identifies a set of tests", + "type": "string", + "format": "uri" + }, + "description": { + "description": "The test set description", + "type": "string" + }, + "comment": { + "description": "Any additional comments about the test set", + "type": "string" + }, + "schema": { + "description": "This should be a valid schema. This should be a ref to a meta-schema if schema keywords need testing." + }, + "tests": { + "description": "A set of related tests all using the same schema", + "type": "array", + "items": { "$ref": "#/definitions/test" }, + "minItems": 1 + } + }, + "additionalProperties": false, + "minItems": 1 + }, + "definitions": { + "outputItem": { + "type": "object", + "properties": { + "valid": { "type": "boolean" }, + "keywordLocation": { "type": "string" }, + "absoluteKeywordLocation": { + "type": "string", + "format": "uri" + }, + "instanceLocation": { "type": "string" }, + "annotations": { + "type": "array", + "items": { "$ref": "#/definitions/outputItem" } + }, + "errors": { + "type": "array", + "items": { "$ref": "#/definitions/outputItem" } + } + } + }, + "test": { + "description": "A single test", + "type": "object", + "required": [ "description", "data", "valid" ], + "properties": { + "id": { + "description": "Uniquely identifies a single test", + "type": "string", + "format": "uri" + }, + "description": { + "description": "The test description", + "type": "string" + }, + "comment": { + "description": "Any additional comments about the test", + "type": "string" + }, + "data": { + "description": "This is the instance to be validated against the schema in \"schema\"." + }, + "valid": { "type": "boolean" }, + "output": { + "type": "object", + "required": [ "basic", "detailed", "verbose" ], + "properties": { + "basic": { "$ref": "#/definitions/outputItem" }, + "detailed": { "$ref": "#/definitions/outputItem" }, + "verbose": { "$ref": "#/definitions/outputItem" } + } + } + }, + "additionalProperties": false + } + } +} diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalItems.json new file mode 100755 index 00000000..ee46b61a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalItems.json @@ -0,0 +1,130 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, valid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, null ], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, invalid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" }, { "type": "string" } ] } + ], + "items": [ {"type": "integer" } ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, "hello" ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalProperties.json new file mode 100755 index 00000000..381275a5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/additionalProperties.json @@ -0,0 +1,133 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "allOf": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in allOf are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/allOf.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/allOf.json new file mode 100755 index 00000000..ec9319e1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/allOf.json @@ -0,0 +1,294 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with one empty schema", + "schema": { + "allOf": [ + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with two empty schemas", + "schema": { + "allOf": [ + {}, + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with the first empty schema", + "schema": { + "allOf": [ + {}, + { "type": "number" } + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with the last empty schema", + "schema": { + "allOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "nested allOf, to check validation semantics", + "schema": { + "allOf": [ + { + "allOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "allOf combined with anyOf, oneOf", + "schema": { + "allOf": [ { "multipleOf": 2 } ], + "anyOf": [ { "multipleOf": 3 } ], + "oneOf": [ { "multipleOf": 5 } ] + }, + "tests": [ + { + "description": "allOf: false, anyOf: false, oneOf: false", + "data": 1, + "valid": false + }, + { + "description": "allOf: false, anyOf: false, oneOf: true", + "data": 5, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: false", + "data": 3, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: true", + "data": 15, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: false", + "data": 2, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: true", + "data": 10, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: false", + "data": 6, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: true", + "data": 30, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/anchor.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/anchor.json new file mode 100755 index 00000000..42dde7e4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/anchor.json @@ -0,0 +1,81 @@ +[ + { + "description": "Location-independent identifier", + "schema": { + "$ref": "#foo", + "$defs": { + "A": { + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "$ref": "http://localhost:1234/bar#foo", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar", + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/nested.json#foo", + "$defs": { + "A": { + "$id": "nested.json", + "$defs": { + "B": { + "$anchor": "foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/anyOf.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/anyOf.json new file mode 100755 index 00000000..ab5eb386 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/anyOf.json @@ -0,0 +1,189 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "anyOf with one empty schema", + "schema": { + "anyOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is valid", + "data": 123, + "valid": true + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/boolean_schema.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/boolean_schema.json new file mode 100755 index 00000000..6d40f23f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/const.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/const.json new file mode 100755 index 00000000..1c2cafcc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/const.json @@ -0,0 +1,342 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + }, + { + "description": "const with false does not match 0", + "schema": {"const": false}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "const with true does not match 1", + "schema": {"const": true}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "const with [false] does not match [0]", + "schema": {"const": [false]}, + "tests": [ + { + "description": "[false] is valid", + "data": [false], + "valid": true + }, + { + "description": "[0] is invalid", + "data": [0], + "valid": false + }, + { + "description": "[0.0] is invalid", + "data": [0.0], + "valid": false + } + ] + }, + { + "description": "const with [true] does not match [1]", + "schema": {"const": [true]}, + "tests": [ + { + "description": "[true] is valid", + "data": [true], + "valid": true + }, + { + "description": "[1] is invalid", + "data": [1], + "valid": false + }, + { + "description": "[1.0] is invalid", + "data": [1.0], + "valid": false + } + ] + }, + { + "description": "const with {\"a\": false} does not match {\"a\": 0}", + "schema": {"const": {"a": false}}, + "tests": [ + { + "description": "{\"a\": false} is valid", + "data": {"a": false}, + "valid": true + }, + { + "description": "{\"a\": 0} is invalid", + "data": {"a": 0}, + "valid": false + }, + { + "description": "{\"a\": 0.0} is invalid", + "data": {"a": 0.0}, + "valid": false + } + ] + }, + { + "description": "const with {\"a\": true} does not match {\"a\": 1}", + "schema": {"const": {"a": true}}, + "tests": [ + { + "description": "{\"a\": true} is valid", + "data": {"a": true}, + "valid": true + }, + { + "description": "{\"a\": 1} is invalid", + "data": {"a": 1}, + "valid": false + }, + { + "description": "{\"a\": 1.0} is invalid", + "data": {"a": 1.0}, + "valid": false + } + ] + }, + { + "description": "const with 0 does not match other zero-like types", + "schema": {"const": 0}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "empty string is invalid", + "data": "", + "valid": false + } + ] + }, + { + "description": "const with 1 does not match true", + "schema": {"const": 1}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "const with -2.0 matches integer and float types", + "schema": {"const": -2.0}, + "tests": [ + { + "description": "integer -2 is valid", + "data": -2, + "valid": true + }, + { + "description": "integer 2 is invalid", + "data": 2, + "valid": false + }, + { + "description": "float -2.0 is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float 2.0 is invalid", + "data": 2.0, + "valid": false + }, + { + "description": "float -2.00001 is invalid", + "data": -2.00001, + "valid": false + } + ] + }, + { + "description": "float and integers are equal up to 64-bit representation limits", + "schema": {"const": 9007199254740992}, + "tests": [ + { + "description": "integer is valid", + "data": 9007199254740992, + "valid": true + }, + { + "description": "integer minus one is invalid", + "data": 9007199254740991, + "valid": false + }, + { + "description": "float is valid", + "data": 9007199254740992.0, + "valid": true + }, + { + "description": "float minus one is invalid", + "data": 9007199254740991.0, + "valid": false + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "const": "hello\u0000there" }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/contains.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/contains.json new file mode 100755 index 00000000..c5471cc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/contains.json @@ -0,0 +1,129 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true + } + ] + }, + { + "description": "items + contains", + "schema": { + "items": { "multipleOf": 2 }, + "contains": { "multipleOf": 3 } + }, + "tests": [ + { + "description": "matches items, does not match contains", + "data": [ 2, 4, 8 ], + "valid": false + }, + { + "description": "does not match items, matches contains", + "data": [ 3, 6, 9 ], + "valid": false + }, + { + "description": "matches both items and contains", + "data": [ 6, 12 ], + "valid": true + }, + { + "description": "matches neither items nor contains", + "data": [ 1, 5 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/content.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/content.json new file mode 100755 index 00000000..44688e82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/content.json @@ -0,0 +1,127 @@ +[ + { + "description": "validation of string-encoded content based on media type", + "schema": { + "contentMediaType": "application/json" + }, + "tests": [ + { + "description": "a valid JSON document", + "data": "{\"foo\": \"bar\"}", + "valid": true + }, + { + "description": "an invalid JSON document; validates true", + "data": "{:}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary string-encoding", + "schema": { + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64 string", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "an invalid base64 string (% is not a valid character); validates true", + "data": "eyJmb28iOi%iYmFyIn0K", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary-encoded media type documents", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document; validates true", + "data": "ezp9Cg==", + "valid": true + }, + { + "description": "an invalid base64 string that is valid JSON; validates true", + "data": "{}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary-encoded media type documents with schema", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64", + "contentSchema": { "required": ["foo"], "properties": { "foo": { "type": "string" } } } + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "another valid base64-encoded JSON document", + "data": "eyJib28iOiAyMCwgImZvbyI6ICJiYXoifQ==", + "valid": true + }, + { + "description": "an invalid base64-encoded JSON document; validates true", + "data": "eyJib28iOiAyMH0=", + "valid": true + }, + { + "description": "an empty object as a base64-encoded JSON document; validates true", + "data": "e30=", + "valid": true + }, + { + "description": "an empty array as a base64-encoded JSON document", + "data": "W10=", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document; validates true", + "data": "ezp9Cg==", + "valid": true + }, + { + "description": "an invalid base64 string that is valid JSON; validates true", + "data": "{}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/default.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/defs.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/defs.json new file mode 100755 index 00000000..f2fbec42 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/defs.json @@ -0,0 +1,24 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "valid definition schema", + "data": {"$defs": {"foo": {"type": "integer"}}}, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "invalid definition schema", + "data": {"$defs": {"foo": {"type": 1}}}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentRequired.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentRequired.json new file mode 100755 index 00000000..c817120d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentRequired.json @@ -0,0 +1,142 @@ +[ + { + "description": "single dependency", + "schema": {"dependentRequired": {"bar": ["foo"]}}, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "empty dependents", + "schema": {"dependentRequired": {"bar": []}}, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "non-object is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "multiple dependents required", + "schema": {"dependentRequired": {"quux": ["foo", "bar"]}}, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependentRequired": { + "foo\nbar": ["foo\rbar"], + "foo\"bar": ["foo'bar"] + } + }, + "tests": [ + { + "description": "CRLF", + "data": { + "foo\nbar": 1, + "foo\rbar": 2 + }, + "valid": true + }, + { + "description": "quoted quotes", + "data": { + "foo'bar": 1, + "foo\"bar": 2 + }, + "valid": true + }, + { + "description": "CRLF missing dependent", + "data": { + "foo\nbar": 1, + "foo": 2 + }, + "valid": false + }, + { + "description": "quoted quotes missing dependent", + "data": { + "foo\"bar": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentSchemas.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentSchemas.json new file mode 100755 index 00000000..e7921d1e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/dependentSchemas.json @@ -0,0 +1,114 @@ +[ + { + "description": "single dependency", + "schema": { + "dependentSchemas": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "boolean subschemas", + "schema": { + "dependentSchemas": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependentSchemas": { + "foo\tbar": {"minProperties": 4}, + "foo'bar": {"required": ["foo\"bar"]} + } + }, + "tests": [ + { + "description": "quoted tab", + "data": { + "foo\tbar": 1, + "a": 2, + "b": 3, + "c": 4 + }, + "valid": true + }, + { + "description": "quoted quote", + "data": { + "foo'bar": {"foo\"bar": 1} + }, + "valid": false + }, + { + "description": "quoted tab invalid under dependent schema", + "data": { + "foo\tbar": 1, + "a": 2 + }, + "valid": false + }, + { + "description": "quoted quote invalid under dependent schema", + "data": {"foo'bar": 1}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/enum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/enum.json new file mode 100755 index 00000000..f085097b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/enum.json @@ -0,0 +1,236 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + }, + { + "description": "valid object matches", + "data": {"foo": 12}, + "valid": true + }, + { + "description": "extra properties in object is invalid", + "data": {"foo": 12, "boo": 42}, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "enum with escaped characters", + "schema": { + "enum": ["foo\nbar", "foo\rbar"] + }, + "tests": [ + { + "description": "member 1 is valid", + "data": "foo\nbar", + "valid": true + }, + { + "description": "member 2 is valid", + "data": "foo\rbar", + "valid": true + }, + { + "description": "another string is invalid", + "data": "abc", + "valid": false + } + ] + }, + { + "description": "enum with false does not match 0", + "schema": {"enum": [false]}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "enum with true does not match 1", + "schema": {"enum": [true]}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "enum with 0 does not match false", + "schema": {"enum": [0]}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + } + ] + }, + { + "description": "enum with 1 does not match true", + "schema": {"enum": [1]}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMaximum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMaximum.json new file mode 100755 index 00000000..dc3cd709 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMinimum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMinimum.json new file mode 100755 index 00000000..b38d7ece --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/format.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/format.json new file mode 100755 index 00000000..dddea866 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/format.json @@ -0,0 +1,686 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of UUIDs", + "schema": { "format": "uuid" }, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of durations", + "schema": { "format": "duration" }, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/id.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/id.json new file mode 100755 index 00000000..cd97d596 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/id.json @@ -0,0 +1,206 @@ +[ + { + "description": "Invalid use of fragments in location-independent $id", + "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/bar#foo", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }, + "valid": false + }, + { + "description": "Identifier path with absolute URI", + "data": { + "$ref": "http://localhost:1234/bar#/a/b", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#/a/b", + "type": "integer" + } + } + }, + "valid": false + }, + { + "description": "Identifier name with base URI change in subschema", + "data": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/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/root", + "$ref": "http://localhost:1234/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": { + "$ref": "https://json-schema.org/draft/2019-09/schema" + }, + "tests": [ + { + "description": "Identifier name with absolute URI", + "data": { + "$ref": "http://localhost:1234/bar", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Identifier name with base URI change in subschema", + "data": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/nested.json#/$defs/B", + "$defs": { + "A": { + "$id": "nested.json", + "$defs": { + "B": { + "$id": "#", + "type": "integer" + } + } + } + } + }, + "valid": true + } + ] + }, + { + "description": "Unnormalized $ids are allowed but discouraged", + "schema": { + "$ref": "https://json-schema.org/draft/2019-09/schema" + }, + "tests": [ + { + "description": "Unnormalized identifier", + "data": { + "$ref": "http://localhost:1234/foo/baz", + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier and no ref", + "data": { + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier with empty fragment", + "data": { + "$ref": "http://localhost:1234/foo/baz", + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz#", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier with empty fragment and no ref", + "data": { + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz#", + "type": "integer" + } + } + }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/if-then-else.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/if-then-else.json new file mode 100755 index 00000000..284e9191 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/if-then-else.json @@ -0,0 +1,258 @@ +[ + { + "description": "ignore if without then or else", + "schema": { + "if": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone if", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone if", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore then without if", + "schema": { + "then": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone then", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone then", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore else without if", + "schema": { + "else": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone else", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone else", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "if and then without else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid when if test fails", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if and else without then", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid when if test passes", + "data": -1, + "valid": true + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "validate against correct branch, then vs else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "non-interference across combined schemas", + "schema": { + "allOf": [ + { + "if": { + "exclusiveMaximum": 0 + } + }, + { + "then": { + "minimum": -10 + } + }, + { + "else": { + "multipleOf": 2 + } + } + ] + }, + "tests": [ + { + "description": "valid, but would have been invalid through then", + "data": -100, + "valid": true + }, + { + "description": "valid, but would have been invalid through else", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if with boolean schema true", + "schema": { + "if": true, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema true in if always chooses the then path (valid)", + "data": "then", + "valid": true + }, + { + "description": "boolean schema true in if always chooses the then path (invalid)", + "data": "else", + "valid": false + } + ] + }, + { + "description": "if with boolean schema false", + "schema": { + "if": false, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema false in if always chooses the else path (invalid)", + "data": "then", + "valid": false + }, + { + "description": "boolean schema false in if always chooses the else path (valid)", + "data": "else", + "valid": true + } + ] + }, + { + "description": "if appears at the end when serialized (keyword processing sequence)", + "schema": { + "then": { "const": "yes" }, + "else": { "const": "other" }, + "if": { "maxLength": 4 } + }, + "tests": [ + { + "description": "yes redirects to then and passes", + "data": "yes", + "valid": true + }, + { + "description": "other redirects to else and passes", + "data": "other", + "valid": true + }, + { + "description": "no redirects to then and fails", + "data": "no", + "valid": false + }, + { + "description": "invalid redirects to else and fails", + "data": "invalid", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/infinite-loop-detection.json new file mode 100755 index 00000000..9c3c3627 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/infinite-loop-detection.json @@ -0,0 +1,36 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "$defs": { + "int": { "type": "integer" } + }, + "allOf": [ + { + "properties": { + "foo": { + "$ref": "#/$defs/int" + } + } + }, + { + "additionalProperties": { + "$ref": "#/$defs/int" + } + } + ] + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/items.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/items.json new file mode 100755 index 00000000..6e98ee82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/items.json @@ -0,0 +1,250 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items and subitems", + "schema": { + "$defs": { + "item": { + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/$defs/sub-item" }, + { "$ref": "#/$defs/sub-item" } + ] + }, + "sub-item": { + "type": "object", + "required": ["foo"] + } + }, + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/$defs/item" }, + { "$ref": "#/$defs/item" }, + { "$ref": "#/$defs/item" } + ] + }, + "tests": [ + { + "description": "valid items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": true + }, + { + "description": "too many items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "too many sub-items", + "data": [ + [ {"foo": null}, {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong item", + "data": [ + {"foo": null}, + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong sub-item", + "data": [ + [ {}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "fewer items is valid", + "data": [ + [ {"foo": null} ], + [ {"foo": null} ] + ], + "valid": true + } + ] + }, + { + "description": "nested items", + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "tests": [ + { + "description": "valid nested array", + "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": true + }, + { + "description": "nested array with invalid type", + "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": false + }, + { + "description": "not deep enough", + "data": [[[1], [2],[3]], [[4], [5], [6]]], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxContains.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxContains.json new file mode 100755 index 00000000..3c42fb31 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxContains.json @@ -0,0 +1,79 @@ +[ + { + "description": "maxContains without contains is ignored", + "schema": { + "maxContains": 1 + }, + "tests": [ + { + "description": "one item valid against lone maxContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "two items still valid against lone maxContains", + "data": [ 1, 2 ], + "valid": true + } + ] + }, + { + "description": "maxContains with contains", + "schema": { + "contains": {"const": 1}, + "maxContains": 1 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, valid maxContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "all elements match, invalid maxContains", + "data": [ 1, 1 ], + "valid": false + }, + { + "description": "some elements match, valid maxContains", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "some elements match, invalid maxContains", + "data": [ 1, 2, 1 ], + "valid": false + } + ] + }, + { + "description": "minContains < maxContains", + "schema": { + "contains": {"const": 1}, + "minContains": 1, + "maxContains": 3 + }, + "tests": [ + { + "description": "actual < minContains < maxContains", + "data": [ ], + "valid": false + }, + { + "description": "minContains < actual < maxContains", + "data": [ 1, 1 ], + "valid": true + }, + { + "description": "minContains < maxContains < actual", + "data": [ 1, 1, 1, 1 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxItems.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxItems.json new file mode 100755 index 00000000..3b53a6b3 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxLength.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxLength.json new file mode 100755 index 00000000..811d35b2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxProperties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxProperties.json new file mode 100755 index 00000000..aa7209f5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maxProperties.json @@ -0,0 +1,54 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "maxProperties = 0 means the object is empty", + "schema": { "maxProperties": 0 }, + "tests": [ + { + "description": "no properties is valid", + "data": {}, + "valid": true + }, + { + "description": "one property is invalid", + "data": { "foo": 1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/maximum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maximum.json new file mode 100755 index 00000000..6844a39e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/maximum.json @@ -0,0 +1,54 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/minContains.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minContains.json new file mode 100755 index 00000000..f359de0f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minContains.json @@ -0,0 +1,172 @@ +[ + { + "description": "minContains without contains is ignored", + "schema": { + "minContains": 1 + }, + "tests": [ + { + "description": "one item valid against lone minContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "zero items still valid against lone minContains", + "data": [], + "valid": true + } + ] + }, + { + "description": "minContains=1 with contains", + "schema": { + "contains": {"const": 1}, + "minContains": 1 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "no elements match", + "data": [ 2 ], + "valid": false + }, + { + "description": "single element matches, valid minContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "some elements match, valid minContains", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "all elements match, valid minContains", + "data": [ 1, 1 ], + "valid": true + } + ] + }, + { + "description": "minContains=2 with contains", + "schema": { + "contains": {"const": 1}, + "minContains": 2 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "some elements match, invalid minContains", + "data": [ 1, 2 ], + "valid": false + }, + { + "description": "all elements match, valid minContains (exactly as needed)", + "data": [ 1, 1 ], + "valid": true + }, + { + "description": "all elements match, valid minContains (more than needed)", + "data": [ 1, 1, 1 ], + "valid": true + }, + { + "description": "some elements match, valid minContains", + "data": [ 1, 2, 1 ], + "valid": true + } + ] + }, + { + "description": "maxContains = minContains", + "schema": { + "contains": {"const": 1}, + "maxContains": 2, + "minContains": 2 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "all elements match, invalid maxContains", + "data": [ 1, 1, 1 ], + "valid": false + }, + { + "description": "all elements match, valid maxContains and minContains", + "data": [ 1, 1 ], + "valid": true + } + ] + }, + { + "description": "maxContains < minContains", + "schema": { + "contains": {"const": 1}, + "maxContains": 1, + "minContains": 3 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "invalid maxContains", + "data": [ 1, 1, 1 ], + "valid": false + }, + { + "description": "invalid maxContains and minContains", + "data": [ 1, 1 ], + "valid": false + } + ] + }, + { + "description": "minContains = 0", + "schema": { + "contains": {"const": 1}, + "minContains": 0 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": true + }, + { + "description": "minContains = 0 makes contains always pass", + "data": [ 2 ], + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/minItems.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minItems.json new file mode 100755 index 00000000..ed511881 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/minLength.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minLength.json new file mode 100755 index 00000000..3f09158d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/minProperties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minProperties.json new file mode 100755 index 00000000..49a0726e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/minimum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minimum.json new file mode 100755 index 00000000..21ae50e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/minimum.json @@ -0,0 +1,69 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/multipleOf.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/multipleOf.json new file mode 100755 index 00000000..faa87cff --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/multipleOf.json @@ -0,0 +1,71 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + }, + { + "description": "invalid instance should not raise error when float division = inf", + "schema": {"type": "integer", "multipleOf": 0.123456789}, + "tests": [ + { + "description": "always invalid, but naive implementations may raise an overflow error", + "data": 1e308, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/not.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/not.json new file mode 100755 index 00000000..98de0eda --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/oneOf.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/oneOf.json new file mode 100755 index 00000000..eeb7ae86 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/oneOf.json @@ -0,0 +1,274 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "oneOf with empty schema", + "schema": { + "oneOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "one valid - valid", + "data": "foo", + "valid": true + }, + { + "description": "both valid - invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "oneOf with required", + "schema": { + "type": "object", + "oneOf": [ + { "required": ["foo", "bar"] }, + { "required": ["foo", "baz"] } + ] + }, + "tests": [ + { + "description": "both invalid - invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "first valid - valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "second valid - valid", + "data": {"foo": 1, "baz": 3}, + "valid": true + }, + { + "description": "both valid - invalid", + "data": {"foo": 1, "bar": 2, "baz" : 3}, + "valid": false + } + ] + }, + { + "description": "oneOf with missing optional property", + "schema": { + "oneOf": [ + { + "properties": { + "bar": true, + "baz": true + }, + "required": ["bar"] + }, + { + "properties": { + "foo": true + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": {"bar": 8}, + "valid": true + }, + { + "description": "second oneOf valid", + "data": {"foo": "foo"}, + "valid": true + }, + { + "description": "both oneOf valid", + "data": {"foo": "foo", "bar": 8}, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": {"baz": "quux"}, + "valid": false + } + ] + }, + { + "description": "nested oneOf, to check validation semantics", + "schema": { + "oneOf": [ + { + "oneOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/bignum.json new file mode 100755 index 00000000..fac275e2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/ecmascript-regex.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/ecmascript-regex.json new file mode 100755 index 00000000..6ed6cbe4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/ecmascript-regex.json @@ -0,0 +1,292 @@ +[ + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\t to horizontal tab", + "schema": { + "type": "string", + "pattern": "^\\t$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\t", + "valid": false + }, + { + "description": "matches", + "data": "\u0009", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\W matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches whitespace", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "Character tabulation matches", + "data": "\t", + "valid": true + }, + { + "description": "Line tabulation matches", + "data": "\u000b", + "valid": true + }, + { + "description": "Form feed matches", + "data": "\u000c", + "valid": true + }, + { + "description": "latin-1 non-breaking-space matches", + "data": "\u00a0", + "valid": true + }, + { + "description": "zero-width whitespace matches", + "data": "\ufeff", + "valid": true + }, + { + "description": "line feed matches (line terminator)", + "data": "\u000a", + "valid": true + }, + { + "description": "paragraph separator matches (line terminator)", + "data": "\u2029", + "valid": true + }, + { + "description": "EM SPACE matches (Space_Separator)", + "data": "\u2003", + "valid": true + }, + { + "description": "Non-whitespace control does not match", + "data": "\u0001", + "valid": false + }, + { + "description": "Non-whitespace does not match", + "data": "\u2013", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "Character tabulation does not match", + "data": "\t", + "valid": false + }, + { + "description": "Line tabulation does not match", + "data": "\u000b", + "valid": false + }, + { + "description": "Form feed does not match", + "data": "\u000c", + "valid": false + }, + { + "description": "latin-1 non-breaking-space does not match", + "data": "\u00a0", + "valid": false + }, + { + "description": "zero-width whitespace does not match", + "data": "\ufeff", + "valid": false + }, + { + "description": "line feed does not match (line terminator)", + "data": "\u000a", + "valid": false + }, + { + "description": "paragraph separator does not match (line terminator)", + "data": "\u2029", + "valid": false + }, + { + "description": "EM SPACE does not match (Space_Separator)", + "data": "\u2003", + "valid": false + }, + { + "description": "Non-whitespace control matches", + "data": "\u0001", + "valid": true + }, + { + "description": "Non-whitespace matches", + "data": "\u2013", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/float-overflow.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/float-overflow.json new file mode 100755 index 00000000..52ff9827 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/float-overflow.json @@ -0,0 +1,13 @@ +[ + { + "description": "all integers are multiples of 0.5, if overflow is handled", + "schema": {"type": "integer", "multipleOf": 0.5}, + "tests": [ + { + "description": "valid if optional overflow handling is implemented", + "data": 1e308, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date-time.json new file mode 100755 index 00000000..900fcb7c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date-time.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "a valid date-time string without second fraction", + "data": "1963-06-19T08:30:06Z", + "valid": true + }, + { + "description": "a valid date-time string with plus offset", + "data": "1937-01-01T12:00:27.87+00:20", + "valid": true + }, + { + "description": "a valid date-time string with minus offset", + "data": "1990-12-31T15:59:50.123-08:00", + "valid": true + }, + { + "description": "a invalid day in date-time string", + "data": "1990-02-31T15:59:60.123-08:00", + "valid": false + }, + { + "description": "an invalid offset in date-time string", + "data": "1990-12-31T15:59:60-24:00", + "valid": false + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date.json new file mode 100755 index 00000000..453b51de --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/date.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "a valid date string", + "data": "1963-06-19", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350", + "valid": false + }, + { + "description": "invalidates non-padded month dates", + "data": "1998-1-20", + "valid": false + }, + { + "description": "invalidates non-padded day dates", + "data": "1998-01-1", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/duration.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/duration.json new file mode 100755 index 00000000..45147381 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/duration.json @@ -0,0 +1,93 @@ +[ + { + "description": "validation of duration strings", + "schema": {"format": "duration"}, + "tests": [ + { + "description": "a valid duration string", + "data": "P4DT12H30M5S", + "valid": true + }, + { + "description": "an invalid duration string", + "data": "PT1D", + "valid": false + }, + { + "description": "no elements present", + "data": "P", + "valid": false + }, + { + "description": "no time elements present", + "data": "P1YT", + "valid": false + }, + { + "description": "no date or time elements present", + "data": "PT", + "valid": false + }, + { + "description": "elements out of order", + "data": "P2D1Y", + "valid": false + }, + { + "description": "missing time separator", + "data": "P1D2H", + "valid": false + }, + { + "description": "time element in the date position", + "data": "P2S", + "valid": false + }, + { + "description": "four years duration", + "data": "P4Y", + "valid": true + }, + { + "description": "zero time, in seconds", + "data": "PT0S", + "valid": true + }, + { + "description": "zero time, in days", + "data": "P0D", + "valid": true + }, + { + "description": "one month duration", + "data": "P1M", + "valid": true + }, + { + "description": "one minute duration", + "data": "PT1M", + "valid": true + }, + { + "description": "one and a half days, in hours", + "data": "PT36H", + "valid": true + }, + { + "description": "one and a half days, in days and hours", + "data": "P1DT12H", + "valid": true + }, + { + "description": "two weeks", + "data": "P2W", + "valid": true + }, + { + "description": "weeks cannot be combined with other units", + "data": "P1Y2W", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/hostname.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/hostname.json new file mode 100755 index 00000000..476541a8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/hostname.json @@ -0,0 +1,68 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a valid punycoded IDN hostname", + "data": "xn--4gbwdl.xn--wgbh1c", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-email.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-email.json new file mode 100755 index 00000000..552d1067 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-email.json @@ -0,0 +1,28 @@ +[ + { + "description": "validation of an internationalized e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "a valid idn e-mail (example@example.test in Hangul)", + "data": "실례@실례.테스트", + "valid": true + }, + { + "description": "an invalid idn e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-hostname.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-hostname.json new file mode 100755 index 00000000..bbb257b9 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/idn-hostname.json @@ -0,0 +1,274 @@ +[ + { + "description": "validation of internationalized host names", + "schema": { "format": "idn-hostname" }, + "tests": [ + { + "description": "a valid host name (example.test in Hangul)", + "data": "실례.테스트", + "valid": true + }, + { + "description": "illegal first char U+302E Hangul single dot tone mark", + "data": "〮실례.테스트", + "valid": false + }, + { + "description": "contains illegal char U+302E Hangul single dot tone mark", + "data": "실〮례.테스트", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트", + "valid": false + }, + { + "description": "invalid label, correct Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1", + "data": "-> $1.00 <--", + "valid": false + }, + { + "description": "valid Chinese Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4", + "data": "xn--ihqwcrb4cv8a8dqg056pqjye", + "valid": true + }, + { + "description": "invalid Punycode", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "xn--X", + "valid": false + }, + { + "description": "U-label contains \"--\" in the 3rd and 4th position", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "XN--aa---o47jg78q", + "valid": false + }, + { + "description": "U-label starts with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello", + "valid": false + }, + { + "description": "U-label ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "hello-", + "valid": false + }, + { + "description": "U-label starts and ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello-", + "valid": false + }, + { + "description": "Begins with a Spacing Combining Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0903hello", + "valid": false + }, + { + "description": "Begins with a Nonspacing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0300hello", + "valid": false + }, + { + "description": "Begins with an Enclosing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0488hello", + "valid": false + }, + { + "description": "Exceptions that are PVALID, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u00df\u03c2\u0f0b\u3007", + "valid": true + }, + { + "description": "Exceptions that are PVALID, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u06fd\u06fe", + "valid": true + }, + { + "description": "Exceptions that are DISALLOWED, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u0640\u07fa", + "valid": false + }, + { + "description": "Exceptions that are DISALLOWED, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start", + "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b", + "valid": false + }, + { + "description": "MIDDLE DOT with no preceding 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "a\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing preceding", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with no following 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7a", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing following", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7", + "valid": false + }, + { + "description": "MIDDLE DOT with surrounding 'l's", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7l", + "valid": true + }, + { + "description": "Greek KERAIA not followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375S", + "valid": false + }, + { + "description": "Greek KERAIA not followed by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375", + "valid": false + }, + { + "description": "Greek KERAIA followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375\u03b2", + "valid": true + }, + { + "description": "Hebrew GERESH not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "A\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05d0\u05f3\u05d1", + "valid": true + }, + { + "description": "Hebrew GERSHAYIM not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "A\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05d0\u05f4\u05d1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "def\u30fbabc", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with no other characters", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with Hiragana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u3041", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Katakana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u30a1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u4e08", + "valid": true + }, + { + "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", + "valid": false + }, + { + "description": "Arabic-Indic digits not 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": "\u0628\u0660\u0628", + "valid": true + }, + { + "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9", + "data": "\u06f00", + "valid": true + }, + { + "description": "ZERO WIDTH JOINER not preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u094d\u200d\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1", + "data": "\u0915\u094d\u200c\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement", + "data": "\u0628\u064a\u200c\u0628\u064a", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv4.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv4.json new file mode 100755 index 00000000..8b99b9fb --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv4.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + }, + { + "description": "an IP address as an integer (decimal)", + "data": "2130706433", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv6.json new file mode 100755 index 00000000..2a08cb46 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/ipv6.json @@ -0,0 +1,153 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::42:ff:1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "missing leading octet is invalid", + "data": ":2:3:4:5:6:7:8", + "valid": false + }, + { + "description": "missing trailing octet is invalid", + "data": "1:2:3:4:5:6:7:", + "valid": false + }, + { + "description": "missing leading octet with omitted octets later", + "data": ":2:3:4::8", + "valid": false + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + }, + { + "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)", + "data": "::ffff:192.168.0.1", + "valid": true + }, + { + "description": "triple colons is invalid", + "data": "1:2:3:4:5:::8", + "valid": false + }, + { + "description": "8 octets", + "data": "1:2:3:4:5:6:7:8", + "valid": true + }, + { + "description": "insufficient octets without double colons", + "data": "1:2:3:4:5:6:7", + "valid": false + }, + { + "description": "no colons is invalid", + "data": "1", + "valid": false + }, + { + "description": "ipv4 is not ipv6", + "data": "127.0.0.1", + "valid": false + }, + { + "description": "ipv4 segment must have 4 octets", + "data": "1:2:3:4:1.2.3", + "valid": false + }, + { + "description": "leading whitespace is invalid", + "data": " ::1", + "valid": false + }, + { + "description": "trailing whitespace is invalid", + "data": "::1 ", + "valid": false + }, + { + "description": "netmask is not a part of ipv6 address", + "data": "fe80::/64", + "valid": false + }, + { + "description": "zone id is not a part of ipv6 address", + "data": "fe80::a%eth1", + "valid": false + }, + { + "description": "a long valid ipv6", + "data": "1000:1000:1000:1000:1000:1000:255.255.255.255", + "valid": true + }, + { + "description": "a long invalid ipv6, below length limit, first", + "data": "100:100:100:100:100:100:255.255.255.255.255", + "valid": false + }, + { + "description": "a long invalid ipv6, below length limit, second", + "data": "100:100:100:100:100:100:100:255.255.255.255", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri-reference.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri-reference.json new file mode 100755 index 00000000..1fd779c2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of IRI References", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "a valid IRI", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid protocol-relative IRI Reference", + "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid relative IRI Reference", + "data": "/âππ", + "valid": true + }, + { + "description": "an invalid IRI Reference", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "a valid IRI Reference", + "data": "âππ", + "valid": true + }, + { + "description": "a valid IRI fragment", + "data": "#ƒrägmênt", + "valid": true + }, + { + "description": "an invalid IRI fragment", + "data": "#ƒräg\\mênt", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri.json new file mode 100755 index 00000000..ed54094c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/iri.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "a valid IRI with anchor tag", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid IRI with anchor tag and parantheses", + "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", + "valid": true + }, + { + "description": "a valid IRI with URL-encoded stuff", + "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid IRI with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid IRI based on IPv6", + "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", + "valid": true + }, + { + "description": "an invalid IRI based on IPv6", + "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "valid": false + }, + { + "description": "an invalid relative IRI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid IRI", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "an invalid IRI though valid IRI reference", + "data": "âππ", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/json-pointer.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/json-pointer.json new file mode 100755 index 00000000..65c2f064 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/json-pointer.json @@ -0,0 +1,168 @@ +[ + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/regex.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/regex.json new file mode 100755 index 00000000..d99d021e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/regex.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "a valid regular expression", + "data": "([abc])+\\s+$", + "valid": true + }, + { + "description": "a regular expression with unclosed parens is invalid", + "data": "^(abc]", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/relative-json-pointer.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/relative-json-pointer.json new file mode 100755 index 00000000..22fb14e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/relative-json-pointer.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of Relative JSON Pointers (RJP)", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "a valid upwards RJP", + "data": "1", + "valid": true + }, + { + "description": "a valid downwards RJP", + "data": "0/foo/bar", + "valid": true + }, + { + "description": "a valid up and then down RJP, with array index", + "data": "2/0/baz/1/zip", + "valid": true + }, + { + "description": "a valid RJP taking the member or index name", + "data": "0#", + "valid": true + }, + { + "description": "an invalid RJP that is a valid JSON Pointer", + "data": "/foo/bar", + "valid": false + }, + { + "description": "negative prefix", + "data": "-1/foo/bar", + "valid": false + }, + { + "description": "## is not a valid json-pointer", + "data": "0##", + "valid": false + }, + { + "description": "zero cannot be followed by other digits, plus json-pointer", + "data": "01/a", + "valid": false + }, + { + "description": "zero cannot be followed by other digits, plus octothorpe", + "data": "01#", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/time.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/time.json new file mode 100755 index 00000000..4ec8a01a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/time.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "a valid time string", + "data": "08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid time string", + "data": "08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "01:01:01,1111", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-reference.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-reference.json new file mode 100755 index 00000000..e4c9eef6 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-template.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-template.json new file mode 100755 index 00000000..33ab76ee --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri-template.json @@ -0,0 +1,28 @@ +[ + { + "description": "format: uri-template", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri.json new file mode 100755 index 00000000..4306a686 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uri.json @@ -0,0 +1,108 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + }, + { + "description": "an invalid URI with comma in scheme", + "data": "bar,baz:foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uuid.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uuid.json new file mode 100755 index 00000000..45bf349b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/uuid.json @@ -0,0 +1,70 @@ +[ + { + "description": "uuid format", + "schema": { + "format": "uuid" + }, + "tests": [ + { + "description": "all upper-case", + "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380", + "valid": true + }, + { + "description": "all lower-case", + "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380", + "valid": true + }, + { + "description": "mixed case", + "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380", + "valid": true + }, + { + "description": "all zeroes is valid", + "data": "00000000-0000-0000-0000-000000000000", + "valid": true + }, + { + "description": "wrong length", + "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638", + "valid": false + }, + { + "description": "missing section", + "data": "2eb8aa08-aa98-11ea-73b441d16380", + "valid": false + }, + { + "description": "bad characters (not hex)", + "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380", + "valid": false + }, + { + "description": "no dashes", + "data": "2eb8aa08aa9811eab4aa73b441d16380", + "valid": false + }, + { + "description": "valid version 4", + "data": "98d80576-482e-427f-8434-7f86890ab222", + "valid": true + }, + { + "description": "valid version 5", + "data": "99c17cbb-656f-564a-940f-1a4568f03487", + "valid": true + }, + { + "description": "hypothetical version 6", + "data": "99c17cbb-656f-664a-940f-1a4568f03487", + "valid": true + }, + { + "description": "hypothetical version 15", + "data": "99c17cbb-656f-f64a-940f-1a4568f03487", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/refOfUnknownKeyword.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/refOfUnknownKeyword.json new file mode 100755 index 00000000..5b150df8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/optional/refOfUnknownKeyword.json @@ -0,0 +1,44 @@ +[ + { + "description": "reference of a root arbitrary keyword ", + "schema": { + "unknown-keyword": {"type": "integer"}, + "properties": { + "bar": {"$ref": "#/unknown-keyword"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "reference of an arbitrary keyword of a sub-schema", + "schema": { + "properties": { + "foo": {"unknown-keyword": {"type": "integer"}}, + "bar": {"$ref": "#/properties/foo/unknown-keyword"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/pattern.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/pattern.json new file mode 100755 index 00000000..92db0f97 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/pattern.json @@ -0,0 +1,59 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid", + "data": "abc", + "valid": false + }, + { + "description": "ignores booleans", + "data": true, + "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/patternProperties.json new file mode 100755 index 00000000..c10ffcc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/patternProperties.json @@ -0,0 +1,156 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "object with a property matching both true and false is invalid", + "data": {"foobar":1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/properties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/properties.json new file mode 100755 index 00000000..b86c1819 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/properties.json @@ -0,0 +1,167 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + }, + { + "description": "properties with escaped characters", + "schema": { + "properties": { + "foo\nbar": {"type": "number"}, + "foo\"bar": {"type": "number"}, + "foo\\bar": {"type": "number"}, + "foo\rbar": {"type": "number"}, + "foo\tbar": {"type": "number"}, + "foo\fbar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with all numbers is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1", + "foo\\bar": "1", + "foo\rbar": "1", + "foo\tbar": "1", + "foo\fbar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/propertyNames.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/propertyNames.json new file mode 100755 index 00000000..8423690d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/recursiveRef.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/recursiveRef.json new file mode 100755 index 00000000..1a221ec1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/recursiveRef.json @@ -0,0 +1,356 @@ +[ + { + "description": "$recursiveRef without $recursiveAnchor works like $ref", + "schema": { + "properties": { + "foo": { "$recursiveRef": "#" } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": { "foo": { "foo": false } }, + "valid": true + }, + { + "description": "mismatch", + "data": { "bar": false }, + "valid": false + }, + { + "description": "recursive mismatch", + "data": { "foo": { "bar": false } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef without using nesting", + "schema": { + "$id": "http://localhost:4242/recursiveRef2/schema.json", + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, no match", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with nesting", + "schema": { + "$id": "http://localhost:4242/recursiveRef3/schema.json", + "$recursiveAnchor": true, + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer now matches as a property value", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, properties match with $recursiveRef", + "data": { "foo": { "bar": 1 } }, + "valid": true + } + ] + }, + { + "description": "$recursiveRef with $recursiveAnchor: false works like $ref", + "schema": { + "$id": "http://localhost:4242/recursiveRef4/schema.json", + "$recursiveAnchor": false, + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": false, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, integer does not match as a property value", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor works like $ref", + "schema": { + "$id": "http://localhost:4242/recursiveRef5/schema.json", + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": false, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, integer does not match as a property value", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor in the initial target schema resource", + "schema": { + "$id": "http://localhost:4242/recursiveRef6/base.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "boolean" }, + { + "type": "object", + "additionalProperties": { + "$id": "http://localhost:4242/recursiveRef6/inner.json", + "$comment": "there is no $recursiveAnchor: true here, so we do NOT recurse to the base", + "anyOf": [ + { "type": "integer" }, + { "type": "object", "additionalProperties": { "$recursiveRef": "#" } } + ] + } + } + ] + }, + "tests": [ + { + "description": "leaf node does not match; no recursion", + "data": { "foo": true }, + "valid": false + }, + { + "description": "leaf node matches: recursion uses the inner schema", + "data": { "foo": { "bar": 1 } }, + "valid": true + }, + { + "description": "leaf node does not match: recursion uses the inner schema", + "data": { "foo": { "bar": true } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor in the outer schema resource", + "schema": { + "$id": "http://localhost:4242/recursiveRef7/base.json", + "anyOf": [ + { "type": "boolean" }, + { + "type": "object", + "additionalProperties": { + "$id": "http://localhost:4242/recursiveRef7/inner.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "integer" }, + { "type": "object", "additionalProperties": { "$recursiveRef": "#" } } + ] + } + } + ] + }, + "tests": [ + { + "description": "leaf node does not match; no recursion", + "data": { "foo": true }, + "valid": false + }, + { + "description": "leaf node matches: recursion only uses inner schema", + "data": { "foo": { "bar": 1 } }, + "valid": true + }, + { + "description": "leaf node does not match: recursion only uses inner schema", + "data": { "foo": { "bar": true } }, + "valid": false + } + ] + }, + { + "description": "multiple dynamic paths to the $recursiveRef keyword", + "schema": { + "$id": "recursiveRef8_main.json", + "$defs": { + "inner": { + "$id": "recursiveRef8_inner.json", + "$recursiveAnchor": true, + "title": "inner", + "additionalProperties": { + "$recursiveRef": "#" + } + } + }, + "if": { + "propertyNames": { + "pattern": "^[a-m]" + } + }, + "then": { + "title": "any type of node", + "$id": "recursiveRef8_anyLeafNode.json", + "$recursiveAnchor": true, + "$ref": "recursiveRef8_main.json#/$defs/inner" + }, + "else": { + "title": "integer node", + "$id": "recursiveRef8_integerNode.json", + "$recursiveAnchor": true, + "type": [ "object", "integer" ], + "$ref": "recursiveRef8_main.json#/$defs/inner" + } + }, + "tests": [ + { + "description": "recurse to anyLeafNode - floats are allowed", + "data": { "alpha": 1.1 }, + "valid": true + }, + { + "description": "recurse to integerNode - floats are not allowed", + "data": { "november": 1.1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/ref.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/ref.json new file mode 100755 index 00000000..2da81e35 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/ref.json @@ -0,0 +1,434 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "$defs": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, + "properties": { + "tilde": {"$ref": "#/$defs/tilde~0field"}, + "slash": {"$ref": "#/$defs/slash~1field"}, + "percent": {"$ref": "#/$defs/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "$defs": { + "a": {"type": "integer"}, + "b": {"$ref": "#/$defs/a"}, + "c": {"$ref": "#/$defs/b"} + }, + "$ref": "#/$defs/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref applies alongside sibling keywords", + "schema": { + "$defs": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/$defs/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid, maxItems valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems invalid", + "data": { "foo": [1, 2, 3] }, + "valid": false + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/$defs/is-string"} + }, + "$defs": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/$defs/bool", + "$defs": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/$defs/bool", + "$defs": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "$defs": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + }, + { + "description": "refs with quote", + "schema": { + "properties": { + "foo\"bar": {"$ref": "#/$defs/foo%22bar"} + }, + "$defs": { + "foo\"bar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with numbers is valid", + "data": { + "foo\"bar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\"bar": "1" + }, + "valid": false + } + ] + }, + { + "description": "ref creates new scope when adjacent to keywords", + "schema": { + "$defs": { + "A": { + "unevaluatedProperties": false + } + }, + "properties": { + "prop1": { + "type": "string" + } + }, + "$ref": "#/$defs/A" + }, + "tests": [ + { + "description": "referenced subschema doesn't see annotations from properties", + "data": { + "prop1": "match" + }, + "valid": false + } + ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "$defs": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/$defs/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/$defs/a_string" }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/refRemote.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/refRemote.json new file mode 100755 index 00000000..b9c6a282 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/refRemote.json @@ -0,0 +1,167 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "baseUriChange/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": {"list": {"$ref": "baseUriChangeFolder/"}}, + "$defs": { + "baz": { + "$id": "baseUriChangeFolder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}}, + "$defs": { + "baz": { + "$id": "baseUriChangeFolderInSubschema/", + "$defs": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name-defs.json#/$defs/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/required.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/required.json new file mode 100755 index 00000000..abf18f34 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/required.json @@ -0,0 +1,105 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with escaped characters", + "schema": { + "required": [ + "foo\nbar", + "foo\"bar", + "foo\\bar", + "foo\rbar", + "foo\tbar", + "foo\fbar" + ] + }, + "tests": [ + { + "description": "object with all properties present is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with some properties missing is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/type.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/type.json new file mode 100755 index 00000000..83046470 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/type.json @@ -0,0 +1,474 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is an integer", + "data": 1.0, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is a number (and an integer)", + "data": 1.0, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an empty string is still a string", + "data": "", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "zero is not a boolean", + "data": 0, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not a boolean", + "data": "", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "true is a boolean", + "data": true, + "valid": true + }, + { + "description": "false is a boolean", + "data": false, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "zero is not null", + "data": 0, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not null", + "data": "", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "true is not null", + "data": true, + "valid": false + }, + { + "description": "false is not null", + "data": false, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type as array with one item", + "schema": { + "type": ["string"] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "type: array or object", + "schema": { + "type": ["array", "object"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type: array, object or null", + "schema": { + "type": ["array", "object", "null"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedItems.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedItems.json new file mode 100755 index 00000000..32f13f82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedItems.json @@ -0,0 +1,437 @@ +[ + { + "description": "unevaluatedItems true", + "schema": { + "type": "array", + "unevaluatedItems": true + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems false", + "schema": { + "type": "array", + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems as schema", + "schema": { + "type": "array", + "unevaluatedItems": { "type": "string" } + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with valid unevaluated items", + "data": ["foo"], + "valid": true + }, + { + "description": "with invalid unevaluated items", + "data": [42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with uniform items", + "schema": { + "type": "array", + "items": { "type": "string" }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "unevaluatedItems doesn't apply", + "data": ["foo", "bar"], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with tuple", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with additionalItems", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "additionalItems": true, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "unevaluatedItems doesn't apply", + "data": ["foo", 42], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with nested tuple", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "allOf": [ + { + "items": [ + true, + { "type": "number" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", 42], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", 42, true], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with nested additionalItems", + "schema": { + "type": "array", + "allOf": [ + { + "items": [ + { "type": "string" } + ], + "additionalItems": true + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no additional items", + "data": ["foo"], + "valid": true + }, + { + "description": "with additional items", + "data": ["foo", 42, true], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with nested unevaluatedItems", + "schema": { + "type": "array", + "allOf": [ + { + "items": [ + { "type": "string" } + ] + }, + { + "unevaluatedItems": true + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no additional items", + "data": ["foo"], + "valid": true + }, + { + "description": "with additional items", + "data": ["foo", 42, true], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with anyOf", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "anyOf": [ + { + "items": [ + true, + { "const": "bar" } + ] + }, + { + "items": [ + true, + true, + { "const": "baz" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "when one schema matches and has no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "when one schema matches and has unevaluated items", + "data": ["foo", "bar", 42], + "valid": false + }, + { + "description": "when two schemas match and has no unevaluated items", + "data": ["foo", "bar", "baz"], + "valid": true + }, + { + "description": "when two schemas match and has unevaluated items", + "data": ["foo", "bar", "baz", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with oneOf", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "oneOf": [ + { + "items": [ + true, + { "const": "bar" } + ] + }, + { + "items": [ + true, + { "const": "baz" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with not", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "not": { + "not": { + "items": [ + true, + { "const": "bar" } + ] + } + }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with unevaluated items", + "data": ["foo", "bar"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with if/then/else", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "if": { + "items": [ + true, + { "const": "bar" } + ] + }, + "then": { + "items": [ + true, + true, + { "const": "then" } + ] + }, + "else": { + "items": [ + true, + true, + true, + { "const": "else" } + ] + }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "when if matches and it has no unevaluated items", + "data": ["foo", "bar", "then"], + "valid": true + }, + { + "description": "when if matches and it has unevaluated items", + "data": ["foo", "bar", "then", "else"], + "valid": false + }, + { + "description": "when if doesn't match and it has no unevaluated items", + "data": ["foo", 42, 42, "else"], + "valid": true + }, + { + "description": "when if doesn't match and it has unevaluated items", + "data": ["foo", 42, 42, "else", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with boolean schemas", + "schema": { + "type": "array", + "allOf": [true], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with $ref", + "schema": { + "type": "array", + "$ref": "#/$defs/bar", + "items": [ + { "type": "string" } + ], + "unevaluatedItems": false, + "$defs": { + "bar": { + "items": [ + true, + { "type": "string" } + ] + } + } + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar", "baz"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems can't see inside cousins", + "schema": { + "allOf": [ + { + "items": [ true ] + }, + { + "unevaluatedItems": false + } + ] + }, + "tests": [ + { + "description": "always fails", + "data": [ 1 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedProperties.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedProperties.json new file mode 100755 index 00000000..b634be5f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/unevaluatedProperties.json @@ -0,0 +1,813 @@ +[ + { + "description": "unevaluatedProperties true", + "schema": { + "type": "object", + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties schema", + "schema": { + "type": "object", + "unevaluatedProperties": { + "type": "string", + "minLength": 3 + } + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with valid unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with invalid unevaluated properties", + "data": { + "foo": "fo" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties false", + "schema": { + "type": "object", + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent properties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent patternProperties", + "schema": { + "type": "object", + "patternProperties": { + "^foo": { "type": "string" } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent additionalProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "additionalProperties": true, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with nested properties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "properties": { + "bar": { "type": "string" } + } + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with nested patternProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "patternProperties": { + "^bar": { "type": "string" } + } + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with nested additionalProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "additionalProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with nested unevaluatedProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": { + "type": "string", + "maxLength": 2 + } + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with anyOf", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "anyOf": [ + { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + }, + { + "properties": { + "baz": { "const": "baz" } + }, + "required": ["baz"] + }, + { + "properties": { + "quux": { "const": "quux" } + }, + "required": ["quux"] + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "when one matches and has no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "when one matches and has unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "not-baz" + }, + "valid": false + }, + { + "description": "when two match and has no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": true + }, + { + "description": "when two match and has unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz", + "quux": "not-quux" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with oneOf", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "oneOf": [ + { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + }, + { + "properties": { + "baz": { "const": "baz" } + }, + "required": ["baz"] + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "quux": "quux" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with not", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "not": { + "not": { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with if/then/else", + "schema": { + "type": "object", + "if": { + "properties": { + "foo": { "const": "then" } + }, + "required": ["foo"] + }, + "then": { + "properties": { + "bar": { "type": "string" } + }, + "required": ["bar"] + }, + "else": { + "properties": { + "baz": { "type": "string" } + }, + "required": ["baz"] + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "when if is true and has no unevaluated properties", + "data": { + "foo": "then", + "bar": "bar" + }, + "valid": true + }, + { + "description": "when if is true and has unevaluated properties", + "data": { + "foo": "then", + "bar": "bar", + "baz": "baz" + }, + "valid": false + }, + { + "description": "when if is false and has no unevaluated properties", + "data": { + "baz": "baz" + }, + "valid": true + }, + { + "description": "when if is false and has unevaluated properties", + "data": { + "foo": "else", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with dependentSchemas", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "dependentSchemas": { + "foo": { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with boolean schemas", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [true], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with $ref", + "schema": { + "type": "object", + "$ref": "#/$defs/bar", + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false, + "$defs": { + "bar": { + "properties": { + "bar": { "type": "string" } + } + } + } + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties can't see inside cousins", + "schema": { + "allOf": [ + { + "properties": { + "foo": true + } + }, + { + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "always fails", + "data": { + "foo": 1 + }, + "valid": false + } + ] + }, + { + "description": "nested unevaluatedProperties, outer false, inner true, properties outside", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "nested unevaluatedProperties, outer false, inner true, properties inside", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "nested unevaluatedProperties, outer true, inner false, properties outside", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": false + } + ], + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "nested unevaluatedProperties, outer true, inner false, properties inside", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + } + ], + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "cousin unevaluatedProperties, true and false, true with properties", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": true + }, + { + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "cousin unevaluatedProperties, true and false, false with properties", + "schema": { + "type": "object", + "allOf": [ + { + "unevaluatedProperties": true + }, + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft2019-09/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/draft2019-09/uniqueItems.json new file mode 100755 index 00000000..4846c773 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft2019-09/uniqueItems.json @@ -0,0 +1,384 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1, "{}"], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + }, + { + "description": "different objects are unique", + "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], + "valid": true + }, + { + "description": "objects are non-unique despite key order", + "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], + "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json old mode 100644 new mode 100755 index 6d4bff51..1e8ebdcc --- a/test/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/additionalItems.json @@ -40,7 +40,22 @@ }, "tests": [ { - "description": "no additional items present", + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", "data": [ 1, 2, 3 ], "valid": true }, @@ -70,13 +85,29 @@ }, { "description": "additionalItems are allowed by default", - "schema": {"items": []}, + "schema": {"items": [{"type": "integer"}]}, "tests": [ { - "description": "only the first items are validated", + "description": "only the first item is validated", "data": [1, "foo", false], "valid": true } ] + }, + { + "description": "additionalItems should not look in applicators", + "schema": { + "extends": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in extends are not examined", + "data": [ 1, null ], + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json old mode 100644 new mode 100755 index c997f75d..46206181 --- a/test/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json @@ -4,6 +4,7 @@ "additionalProperties being false does not allow other properties", "schema": { "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, "additionalProperties": false }, "tests": [ @@ -18,9 +19,43 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", "data": [1, 2, 3], "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false } ] }, @@ -49,6 +84,25 @@ } ] }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, { "description": "additionalProperties are allowed by default", "schema": {"properties": {"foo": {}, "bar": {}}}, @@ -59,5 +113,21 @@ "valid": true } ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "extends": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in extends are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/default.json b/test/JSON-Schema-Test-Suite/tests/draft3/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/dependencies.json b/test/JSON-Schema-Test-Suite/tests/draft3/dependencies.json old mode 100644 new mode 100755 index 2f6ae489..0ffa6bf4 --- a/test/JSON-Schema-Test-Suite/tests/draft3/dependencies.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/dependencies.json @@ -26,8 +26,18 @@ "valid": false }, { - "description": "ignores non-objects", - "data": "foo", + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, "valid": true } ] @@ -88,6 +98,11 @@ "data": {"foo": 1, "bar": 2}, "valid": true }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, { "description": "wrong type", "data": {"foo": "quux", "bar": 2}, diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/disallow.json b/test/JSON-Schema-Test-Suite/tests/draft3/disallow.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json b/test/JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/enum.json b/test/JSON-Schema-Test-Suite/tests/draft3/enum.json old mode 100644 new mode 100755 index a539edb7..5a1ab3b6 --- a/test/JSON-Schema-Test-Suite/tests/draft3/enum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/enum.json @@ -35,5 +35,84 @@ "valid": false } ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"], "required":true} + } + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/extends.json b/test/JSON-Schema-Test-Suite/tests/draft3/extends.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/format.json b/test/JSON-Schema-Test-Suite/tests/draft3/format.json new file mode 100755 index 00000000..82793362 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/format.json @@ -0,0 +1,362 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ip-address"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "host-name"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of CSS colors", + "schema": {"format": "color"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/draft3/infinite-loop-detection.json new file mode 100755 index 00000000..090f49a0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/infinite-loop-detection.json @@ -0,0 +1,32 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "definitions": { + "int": { "type": "integer" } + }, + "properties": { + "foo": { + "$ref": "#/definitions/int" + } + }, + "extends": { + "additionalProperties": { + "$ref": "#/definitions/int" + } + } + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/items.json b/test/JSON-Schema-Test-Suite/tests/draft3/items.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/maxItems.json b/test/JSON-Schema-Test-Suite/tests/draft3/maxItems.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/maxLength.json b/test/JSON-Schema-Test-Suite/tests/draft3/maxLength.json old mode 100644 new mode 100755 index 561767b9..4de42bca --- a/test/JSON-Schema-Test-Suite/tests/draft3/maxLength.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/maxLength.json @@ -22,6 +22,11 @@ "description": "ignores non-strings", "data": 10, "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/maximum.json b/test/JSON-Schema-Test-Suite/tests/draft3/maximum.json old mode 100644 new mode 100755 index 86c7b89c..ccb79c6c --- a/test/JSON-Schema-Test-Suite/tests/draft3/maximum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/maximum.json @@ -8,6 +8,63 @@ "data": 2.6, "valid": true }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + }, + { + "description": "maximum validation (explicit false exclusivity)", + "schema": {"maximum": 3.0, "exclusiveMaximum": false}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, { "description": "above the maximum is invalid", "data": 3.5, diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/minItems.json b/test/JSON-Schema-Test-Suite/tests/draft3/minItems.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/minLength.json b/test/JSON-Schema-Test-Suite/tests/draft3/minLength.json old mode 100644 new mode 100755 index e9c14b17..3f09158d --- a/test/JSON-Schema-Test-Suite/tests/draft3/minLength.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/minLength.json @@ -22,6 +22,11 @@ "description": "ignores non-strings", "data": 1, "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/minimum.json b/test/JSON-Schema-Test-Suite/tests/draft3/minimum.json old mode 100644 new mode 100755 index d5bf000b..d579536e --- a/test/JSON-Schema-Test-Suite/tests/draft3/minimum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/minimum.json @@ -8,6 +8,11 @@ "data": 2.6, "valid": true }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, { "description": "below the minimum is invalid", "data": 0.6, @@ -38,5 +43,46 @@ "valid": false } ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json old mode 100644 new mode 100755 index 7b4755ca..ccc7c17f --- a/test/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json @@ -21,6 +21,28 @@ } ] }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, { "description": "string", "schema": {"type": "string"}, @@ -32,6 +54,17 @@ } ] }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, { "description": "float comparison with high precision", "schema": { @@ -45,5 +78,30 @@ "valid": false } ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "minimum": -972783798187987123879878123.18878137, + "exclusiveMinimum": true + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/ecmascript-regex.json old mode 100644 new mode 100755 similarity index 100% rename from test/JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json rename to test/JSON-Schema-Test-Suite/tests/draft3/optional/ecmascript-regex.json diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format.json deleted file mode 100644 index 1ff461ad..00000000 --- a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format.json +++ /dev/null @@ -1,212 +0,0 @@ -[ - { - "description": "validation of regular expressions", - "schema": {"format": "regex"}, - "tests": [ - { - "description": "a valid regular expression", - "data": "([abc])+\\s+$", - "valid": true - }, - { - "description": "a regular expression with unclosed parens is invalid", - "data": "^(abc]", - "valid": false - } - ] - }, - { - "description": "validation of date-time strings", - "schema": {"format": "date-time"}, - "tests": [ - { - "description": "a valid date-time string", - "data": "1963-06-19T08:30:06.283185Z", - "valid": true - }, - { - "description": "an invalid date-time string", - "data": "06/19/1963 08:30:06 PST", - "valid": false - }, - { - "description": "only RFC3339 not all of ISO 8601 are valid", - "data": "2013-350T01:01:01", - "valid": false - } - ] - }, - { - "description": "validation of date strings", - "schema": {"format": "date"}, - "tests": [ - { - "description": "a valid date string", - "data": "1963-06-19", - "valid": true - }, - { - "description": "an invalid date string", - "data": "06/19/1963", - "valid": false - } - ] - }, - { - "description": "validation of time strings", - "schema": {"format": "time"}, - "tests": [ - { - "description": "a valid time string", - "data": "08:30:06", - "valid": true - }, - { - "description": "an invalid time string", - "data": "8:30 AM", - "valid": false - } - ] - }, - { - "description": "validation of URIs", - "schema": {"format": "uri"}, - "tests": [ - { - "description": "a valid URI", - "data": "http://foo.bar/?baz=qux#quux", - "valid": true - }, - { - "description": "an invalid URI", - "data": "\\\\WINDOWS\\fileshare", - "valid": false - } - ] - }, - { - "description": "validation of e-mail addresses", - "schema": {"format": "email"}, - "tests": [ - { - "description": "a valid e-mail address", - "data": "joe.bloggs@example.com", - "valid": true - }, - { - "description": "an invalid e-mail address", - "data": "2962", - "valid": false - } - ] - }, - { - "description": "validation of IP addresses", - "schema": {"format": "ip-address"}, - "tests": [ - { - "description": "a valid IP address", - "data": "192.168.0.1", - "valid": true - }, - { - "description": "an IP address with too many components", - "data": "127.0.0.0.1", - "valid": false - }, - { - "description": "an IP address with out-of-range values", - "data": "256.256.256.256", - "valid": false - } - ] - }, - { - "description": "validation of IPv6 addresses", - "schema": {"format": "ipv6"}, - "tests": [ - { - "description": "a valid IPv6 address", - "data": "::1", - "valid": true - }, - { - "description": "an IPv6 address with out-of-range values", - "data": "12345::", - "valid": false - }, - { - "description": "an IPv6 address with too many components", - "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", - "valid": false - }, - { - "description": "an IPv6 address containing illegal characters", - "data": "::laptop", - "valid": false - } - ] - }, - { - "description": "validation of host names", - "schema": {"format": "host-name"}, - "tests": [ - { - "description": "a valid host name", - "data": "www.example.com", - "valid": true - }, - { - "description": "a host name starting with an illegal character", - "data": "-a-host-name-that-starts-with--", - "valid": false - }, - { - "description": "a host name containing illegal characters", - "data": "not_a_valid_host_name", - "valid": false - }, - { - "description": "a host name with a component too long", - "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", - "valid": false - } - ] - }, - { - "description": "validation of CSS colors", - "schema": {"format": "color"}, - "tests": [ - { - "description": "a valid CSS color name", - "data": "fuchsia", - "valid": true - }, - { - "description": "a valid six-digit CSS color code", - "data": "#CC8899", - "valid": true - }, - { - "description": "a valid three-digit CSS color code", - "data": "#C89", - "valid": true - }, - { - "description": "an invalid CSS color code", - "data": "#00332520", - "valid": false - }, - { - "description": "an invalid CSS color name", - "data": "puce", - "valid": false - }, - { - "description": "a CSS color name containing invalid characters", - "data": "light_grayish_red-violet", - "valid": false - } - ] - } -] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/color.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/color.json new file mode 100755 index 00000000..e80cb69e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/color.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of CSS colors", + "schema": {"format": "color"}, + "tests": [ + { + "description": "a valid CSS color name", + "data": "fuchsia", + "valid": true + }, + { + "description": "a valid six-digit CSS color code", + "data": "#CC8899", + "valid": true + }, + { + "description": "a valid three-digit CSS color code", + "data": "#C89", + "valid": true + }, + { + "description": "an invalid CSS color code", + "data": "#00332520", + "valid": false + }, + { + "description": "an invalid CSS color name", + "data": "puce", + "valid": false + }, + { + "description": "a CSS color name containing invalid characters", + "data": "light_grayish_red-violet", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date-time.json new file mode 100755 index 00000000..58261fad --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date-time.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date.json new file mode 100755 index 00000000..4842b487 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/date.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "a valid date string", + "data": "1963-06-19", + "valid": true + }, + { + "description": "an invalid date string", + "data": "06/19/1963", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350", + "valid": false + }, + { + "description": "invalidates non-padded month dates", + "data": "1998-1-20", + "valid": false + }, + { + "description": "invalidates non-padded day dates", + "data": "1998-01-1", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/host-name.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/host-name.json new file mode 100755 index 00000000..8f6e28a0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/host-name.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "host-name"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ip-address.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ip-address.json new file mode 100755 index 00000000..c868bfb5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ip-address.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ip-address"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ipv6.json new file mode 100755 index 00000000..131edb82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/ipv6.json @@ -0,0 +1,68 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/regex.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/regex.json new file mode 100755 index 00000000..d99d021e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/regex.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "a valid regular expression", + "data": "([abc])+\\s+$", + "valid": true + }, + { + "description": "a regular expression with unclosed parens is invalid", + "data": "^(abc]", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/time.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/time.json new file mode 100755 index 00000000..e97160de --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/time.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "a valid time string", + "data": "08:30:06", + "valid": true + }, + { + "description": "an invalid time string", + "data": "8:30 AM", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/uri.json new file mode 100755 index 00000000..9c4de354 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/format/uri.json @@ -0,0 +1,28 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft3/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json b/test/JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/pattern.json b/test/JSON-Schema-Test-Suite/tests/draft3/pattern.json old mode 100644 new mode 100755 index befc4b56..92db0f97 --- a/test/JSON-Schema-Test-Suite/tests/draft3/pattern.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/pattern.json @@ -14,9 +14,45 @@ "valid": false }, { - "description": "ignores non-strings", + "description": "ignores booleans", "data": true, "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json old mode 100644 new mode 100755 index 18586e5d..2ca9aaeb --- a/test/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/patternProperties.json @@ -29,7 +29,12 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", "data": 12, "valid": true } diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/properties.json b/test/JSON-Schema-Test-Suite/tests/draft3/properties.json old mode 100644 new mode 100755 index cd1644dc..a830c67e --- a/test/JSON-Schema-Test-Suite/tests/draft3/properties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/properties.json @@ -29,9 +29,14 @@ "valid": true }, { - "description": "ignores non-objects", + "description": "ignores arrays", "data": [], "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true } ] }, diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/ref.json b/test/JSON-Schema-Test-Suite/tests/draft3/ref.json old mode 100644 new mode 100755 index c9840192..ebdb236b --- a/test/JSON-Schema-Test-Suite/tests/draft3/ref.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/ref.json @@ -75,30 +75,47 @@ { "description": "escaped pointer ref", "schema": { - "tilda~field": {"type": "integer"}, - "slash/field": {"type": "integer"}, - "percent%field": {"type": "integer"}, + "definitions": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, "properties": { - "tilda": {"$ref": "#/tilda~0field"}, - "slash": {"$ref": "#/slash~1field"}, - "percent": {"$ref": "#/percent%25field"} + "tilde": {"$ref": "#/definitions/tilde~0field"}, + "slash": {"$ref": "#/definitions/slash~1field"}, + "percent": {"$ref": "#/definitions/percent%25field"} } }, "tests": [ { - "description": "slash", + "description": "slash invalid", "data": {"slash": "aoeu"}, "valid": false }, { - "description": "tilda", - "data": {"tilda": "aoeu"}, + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, "valid": false }, { - "description": "percent", + "description": "percent invalid", "data": {"percent": "aoeu"}, "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true } ] }, @@ -125,6 +142,64 @@ } ] }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "remote ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "remote ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/definitions/is-string"} + }, + "definitions": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, { "description": "remote ref, containing refs itself", "schema": {"$ref": "http://json-schema.org/draft-03/schema#"}, @@ -140,5 +215,28 @@ "valid": false } ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "definitions": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/definitions/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/definitions/a_string" }, + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/refRemote.json b/test/JSON-Schema-Test-Suite/tests/draft3/refRemote.json old mode 100644 new mode 100755 index 4ca80473..de0cb43a --- a/test/JSON-Schema-Test-Suite/tests/draft3/refRemote.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/refRemote.json @@ -54,7 +54,7 @@ "schema": { "id": "http://localhost:1234/", "items": { - "id": "folder/", + "id": "baseUriChange/", "items": {"$ref": "folderInteger.json"} } }, diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/required.json b/test/JSON-Schema-Test-Suite/tests/draft3/required.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/type.json b/test/JSON-Schema-Test-Suite/tests/draft3/type.json old mode 100644 new mode 100755 index 8f108899..f962cc3d --- a/test/JSON-Schema-Test-Suite/tests/draft3/type.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/type.json @@ -18,6 +18,11 @@ "data": "foo", "valid": false }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, { "description": "an object is not an integer", "data": {}, @@ -49,6 +54,11 @@ "data": 1, "valid": true }, + { + "description": "a float with zero fractional part is a number", + "data": 1.0, + "valid": true + }, { "description": "a float is a number", "data": 1.1, @@ -59,6 +69,11 @@ "data": "foo", "valid": false }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, { "description": "an object is not a number", "data": {}, @@ -100,6 +115,11 @@ "data": "foo", "valid": true }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, { "description": "an object is not a string", "data": {}, @@ -188,7 +208,7 @@ "valid": false }, { - "description": "an array is not an array", + "description": "an array is an array", "data": [], "valid": true }, @@ -234,7 +254,7 @@ "valid": false }, { - "description": "a boolean is not a boolean", + "description": "a boolean is a boolean", "data": true, "valid": true }, diff --git a/test/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json old mode 100644 new mode 100755 index c1f4ab99..fd4b8497 --- a/test/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json +++ b/test/JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json @@ -64,6 +64,26 @@ "data": [0, false], "valid": true }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, { "description": "unique heterogeneous types are valid", "data": [{}, [1], true, null, 1], @@ -73,6 +93,261 @@ "description": "non-unique heterogeneous types are invalid", "data": [{}, [1], true, null, {}, 1], "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json old mode 100644 new mode 100755 index 521745c8..ee46b61a --- a/test/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/additionalItems.json @@ -40,7 +40,22 @@ }, "tests": [ { - "description": "no additional items present", + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", "data": [ 1, 2, 3 ], "valid": true }, @@ -78,5 +93,38 @@ "valid": true } ] + }, + { + "description": "additionalItems should not look in applicators, valid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, null ], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, invalid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" }, { "type": "string" } ] } + ], + "items": [ {"type": "integer" } ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, "hello" ], + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json old mode 100644 new mode 100755 index c997f75d..381275a5 --- a/test/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json @@ -4,6 +4,7 @@ "additionalProperties being false does not allow other properties", "schema": { "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, "additionalProperties": false }, "tests": [ @@ -18,9 +19,43 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", "data": [1, 2, 3], "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false } ] }, @@ -49,6 +84,25 @@ } ] }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, { "description": "additionalProperties are allowed by default", "schema": {"properties": {"foo": {}, "bar": {}}}, @@ -59,5 +113,21 @@ "valid": true } ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "allOf": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in allOf are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/allOf.json b/test/JSON-Schema-Test-Suite/tests/draft4/allOf.json old mode 100644 new mode 100755 index bbb5f89e..fc7dec59 --- a/test/JSON-Schema-Test-Suite/tests/draft4/allOf.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/allOf.json @@ -108,5 +108,154 @@ "valid": false } ] + }, + { + "description": "allOf with one empty schema", + "schema": { + "allOf": [ + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with two empty schemas", + "schema": { + "allOf": [ + {}, + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with the first empty schema", + "schema": { + "allOf": [ + {}, + { "type": "number" } + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with the last empty schema", + "schema": { + "allOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "nested allOf, to check validation semantics", + "schema": { + "allOf": [ + { + "allOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "allOf combined with anyOf, oneOf", + "schema": { + "allOf": [ { "multipleOf": 2 } ], + "anyOf": [ { "multipleOf": 3 } ], + "oneOf": [ { "multipleOf": 5 } ] + }, + "tests": [ + { + "description": "allOf: false, anyOf: false, oneOf: false", + "data": 1, + "valid": false + }, + { + "description": "allOf: false, anyOf: false, oneOf: true", + "data": 5, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: false", + "data": 3, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: true", + "data": 15, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: false", + "data": 2, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: true", + "data": 10, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: false", + "data": 6, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: true", + "data": 30, + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/anyOf.json b/test/JSON-Schema-Test-Suite/tests/draft4/anyOf.json old mode 100644 new mode 100755 index a58714af..f8d82e87 --- a/test/JSON-Schema-Test-Suite/tests/draft4/anyOf.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/anyOf.json @@ -64,5 +64,119 @@ "valid": false } ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "anyOf with one empty schema", + "schema": { + "anyOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is valid", + "data": 123, + "valid": true + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/default.json b/test/JSON-Schema-Test-Suite/tests/draft4/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/definitions.json b/test/JSON-Schema-Test-Suite/tests/draft4/definitions.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/dependencies.json b/test/JSON-Schema-Test-Suite/tests/draft4/dependencies.json old mode 100644 new mode 100755 index 7b9b16a7..51eeddf3 --- a/test/JSON-Schema-Test-Suite/tests/draft4/dependencies.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/dependencies.json @@ -26,8 +26,18 @@ "valid": false }, { - "description": "ignores non-objects", - "data": "foo", + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, "valid": true } ] @@ -109,5 +119,76 @@ "valid": false } ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependencies": { + "foo\nbar": ["foo\rbar"], + "foo\tbar": { + "minProperties": 4 + }, + "foo'bar": {"required": ["foo\"bar"]}, + "foo\"bar": ["foo'bar"] + } + }, + "tests": [ + { + "description": "valid object 1", + "data": { + "foo\nbar": 1, + "foo\rbar": 2 + }, + "valid": true + }, + { + "description": "valid object 2", + "data": { + "foo\tbar": 1, + "a": 2, + "b": 3, + "c": 4 + }, + "valid": true + }, + { + "description": "valid object 3", + "data": { + "foo'bar": 1, + "foo\"bar": 2 + }, + "valid": true + }, + { + "description": "invalid object 1", + "data": { + "foo\nbar": 1, + "foo": 2 + }, + "valid": false + }, + { + "description": "invalid object 2", + "data": { + "foo\tbar": 1, + "a": 2 + }, + "valid": false + }, + { + "description": "invalid object 3", + "data": { + "foo'bar": 1 + }, + "valid": false + }, + { + "description": "invalid object 4", + "data": { + "foo\"bar": 2 + }, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/enum.json b/test/JSON-Schema-Test-Suite/tests/draft4/enum.json old mode 100644 new mode 100755 index a539edb7..f085097b --- a/test/JSON-Schema-Test-Suite/tests/draft4/enum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/enum.json @@ -33,6 +33,203 @@ "description": "objects are deep compared", "data": {"foo": false}, "valid": false + }, + { + "description": "valid object matches", + "data": {"foo": 12}, + "valid": true + }, + { + "description": "extra properties in object is invalid", + "data": {"foo": 12, "boo": 42}, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "enum with escaped characters", + "schema": { + "enum": ["foo\nbar", "foo\rbar"] + }, + "tests": [ + { + "description": "member 1 is valid", + "data": "foo\nbar", + "valid": true + }, + { + "description": "member 2 is valid", + "data": "foo\rbar", + "valid": true + }, + { + "description": "another string is invalid", + "data": "abc", + "valid": false + } + ] + }, + { + "description": "enum with false does not match 0", + "schema": {"enum": [false]}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "enum with true does not match 1", + "schema": {"enum": [true]}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "enum with 0 does not match false", + "schema": {"enum": [0]}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + } + ] + }, + { + "description": "enum with 1 does not match true", + "schema": {"enum": [1]}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/format.json b/test/JSON-Schema-Test-Suite/tests/draft4/format.json new file mode 100755 index 00000000..61e4b62a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/format.json @@ -0,0 +1,218 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/draft4/infinite-loop-detection.json new file mode 100755 index 00000000..f98c74fc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/infinite-loop-detection.json @@ -0,0 +1,36 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "definitions": { + "int": { "type": "integer" } + }, + "allOf": [ + { + "properties": { + "foo": { + "$ref": "#/definitions/int" + } + } + }, + { + "additionalProperties": { + "$ref": "#/definitions/int" + } + } + ] + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/items.json b/test/JSON-Schema-Test-Suite/tests/draft4/items.json old mode 100644 new mode 100755 index f5e18a13..7bf9f02b --- a/test/JSON-Schema-Test-Suite/tests/draft4/items.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/items.json @@ -19,6 +19,14 @@ "description": "ignores non-arrays", "data": {"foo" : "bar"}, "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true } ] }, @@ -40,6 +48,147 @@ "description": "wrong types", "data": [ "foo", 1 ], "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items and subitems", + "schema": { + "definitions": { + "item": { + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/sub-item" }, + { "$ref": "#/definitions/sub-item" } + ] + }, + "sub-item": { + "type": "object", + "required": ["foo"] + } + }, + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" } + ] + }, + "tests": [ + { + "description": "valid items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": true + }, + { + "description": "too many items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "too many sub-items", + "data": [ + [ {"foo": null}, {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong item", + "data": [ + {"foo": null}, + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong sub-item", + "data": [ + [ {}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "fewer items is valid", + "data": [ + [ {"foo": null} ], + [ {"foo": null} ] + ], + "valid": true + } + ] + }, + { + "description": "nested items", + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "tests": [ + { + "description": "valid nested array", + "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": true + }, + { + "description": "nested array with invalid type", + "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": false + }, + { + "description": "not deep enough", + "data": [[[1], [2],[3]], [[4], [5], [6]]], + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/maxItems.json b/test/JSON-Schema-Test-Suite/tests/draft4/maxItems.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/maxLength.json b/test/JSON-Schema-Test-Suite/tests/draft4/maxLength.json old mode 100644 new mode 100755 index 561767b9..811d35b2 --- a/test/JSON-Schema-Test-Suite/tests/draft4/maxLength.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/maxLength.json @@ -20,7 +20,12 @@ }, { "description": "ignores non-strings", - "data": 10, + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", "valid": true } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json b/test/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json old mode 100644 new mode 100755 index d282446a..aa7209f5 --- a/test/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/maxProperties.json @@ -19,9 +19,35 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", "data": "foobar", "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "maxProperties = 0 means the object is empty", + "schema": { "maxProperties": 0 }, + "tests": [ + { + "description": "no properties is valid", + "data": {}, + "valid": true + }, + { + "description": "one property is invalid", + "data": { "foo": 1 }, + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/maximum.json b/test/JSON-Schema-Test-Suite/tests/draft4/maximum.json old mode 100644 new mode 100755 index 86c7b89c..ccb79c6c --- a/test/JSON-Schema-Test-Suite/tests/draft4/maximum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/maximum.json @@ -8,6 +8,63 @@ "data": 2.6, "valid": true }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + }, + { + "description": "maximum validation (explicit false exclusivity)", + "schema": {"maximum": 3.0, "exclusiveMaximum": false}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, { "description": "above the maximum is invalid", "data": 3.5, diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/minItems.json b/test/JSON-Schema-Test-Suite/tests/draft4/minItems.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/minLength.json b/test/JSON-Schema-Test-Suite/tests/draft4/minLength.json old mode 100644 new mode 100755 index e9c14b17..3f09158d --- a/test/JSON-Schema-Test-Suite/tests/draft4/minLength.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/minLength.json @@ -22,6 +22,11 @@ "description": "ignores non-strings", "data": 1, "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/minProperties.json b/test/JSON-Schema-Test-Suite/tests/draft4/minProperties.json old mode 100644 new mode 100755 index a72c7d29..49a0726e --- a/test/JSON-Schema-Test-Suite/tests/draft4/minProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/minProperties.json @@ -19,9 +19,19 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", "data": "", "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/minimum.json b/test/JSON-Schema-Test-Suite/tests/draft4/minimum.json old mode 100644 new mode 100755 index d5bf000b..22d310e1 --- a/test/JSON-Schema-Test-Suite/tests/draft4/minimum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/minimum.json @@ -8,6 +8,37 @@ "data": 2.6, "valid": true }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "minimum validation (explicit false exclusivity)", + "schema": {"minimum": 1.1, "exclusiveMinimum": false}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, { "description": "below the minimum is invalid", "data": 0.6, @@ -38,5 +69,46 @@ "valid": false } ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/multipleOf.json b/test/JSON-Schema-Test-Suite/tests/draft4/multipleOf.json old mode 100644 new mode 100755 index ca3b7618..faa87cff --- a/test/JSON-Schema-Test-Suite/tests/draft4/multipleOf.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/multipleOf.json @@ -56,5 +56,16 @@ "valid": false } ] + }, + { + "description": "invalid instance should not raise error when float division = inf", + "schema": {"type": "integer", "multipleOf": 0.123456789}, + "tests": [ + { + "description": "always invalid, but naive implementations may raise an overflow error", + "data": 1e308, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/not.json b/test/JSON-Schema-Test-Suite/tests/draft4/not.json old mode 100644 new mode 100755 index 2cdc9790..cbb7f46b --- a/test/JSON-Schema-Test-Suite/tests/draft4/not.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/not.json @@ -69,5 +69,28 @@ "valid": false } ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] } + ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/oneOf.json b/test/JSON-Schema-Test-Suite/tests/draft4/oneOf.json old mode 100644 new mode 100755 index 1eaa4e47..fb63b089 --- a/test/JSON-Schema-Test-Suite/tests/draft4/oneOf.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/oneOf.json @@ -64,5 +64,167 @@ "valid": false } ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "oneOf with empty schema", + "schema": { + "oneOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "one valid - valid", + "data": "foo", + "valid": true + }, + { + "description": "both valid - invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "oneOf with required", + "schema": { + "type": "object", + "oneOf": [ + { "required": ["foo", "bar"] }, + { "required": ["foo", "baz"] } + ] + }, + "tests": [ + { + "description": "both invalid - invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "first valid - valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "second valid - valid", + "data": {"foo": 1, "baz": 3}, + "valid": true + }, + { + "description": "both valid - invalid", + "data": {"foo": 1, "bar": 2, "baz" : 3}, + "valid": false + } + ] + }, + { + "description": "oneOf with missing optional property", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {}, + "baz": {} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": {"bar": 8}, + "valid": true + }, + { + "description": "second oneOf valid", + "data": {"foo": "foo"}, + "valid": true + }, + { + "description": "both oneOf valid", + "data": {"foo": "foo", "bar": 8}, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": {"baz": "quux"}, + "valid": false + } + ] + }, + { + "description": "nested oneOf, to check validation semantics", + "schema": { + "oneOf": [ + { + "oneOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json old mode 100644 new mode 100755 index 7b4755ca..ccc7c17f --- a/test/JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json @@ -21,6 +21,28 @@ } ] }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, { "description": "string", "schema": {"type": "string"}, @@ -32,6 +54,17 @@ } ] }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, { "description": "float comparison with high precision", "schema": { @@ -45,5 +78,30 @@ "valid": false } ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "minimum": -972783798187987123879878123.18878137, + "exclusiveMinimum": true + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.json new file mode 100755 index 00000000..6ed6cbe4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.json @@ -0,0 +1,292 @@ +[ + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\t to horizontal tab", + "schema": { + "type": "string", + "pattern": "^\\t$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\t", + "valid": false + }, + { + "description": "matches", + "data": "\u0009", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\W matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches whitespace", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "Character tabulation matches", + "data": "\t", + "valid": true + }, + { + "description": "Line tabulation matches", + "data": "\u000b", + "valid": true + }, + { + "description": "Form feed matches", + "data": "\u000c", + "valid": true + }, + { + "description": "latin-1 non-breaking-space matches", + "data": "\u00a0", + "valid": true + }, + { + "description": "zero-width whitespace matches", + "data": "\ufeff", + "valid": true + }, + { + "description": "line feed matches (line terminator)", + "data": "\u000a", + "valid": true + }, + { + "description": "paragraph separator matches (line terminator)", + "data": "\u2029", + "valid": true + }, + { + "description": "EM SPACE matches (Space_Separator)", + "data": "\u2003", + "valid": true + }, + { + "description": "Non-whitespace control does not match", + "data": "\u0001", + "valid": false + }, + { + "description": "Non-whitespace does not match", + "data": "\u2013", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "Character tabulation does not match", + "data": "\t", + "valid": false + }, + { + "description": "Line tabulation does not match", + "data": "\u000b", + "valid": false + }, + { + "description": "Form feed does not match", + "data": "\u000c", + "valid": false + }, + { + "description": "latin-1 non-breaking-space does not match", + "data": "\u00a0", + "valid": false + }, + { + "description": "zero-width whitespace does not match", + "data": "\ufeff", + "valid": false + }, + { + "description": "line feed does not match (line terminator)", + "data": "\u000a", + "valid": false + }, + { + "description": "paragraph separator does not match (line terminator)", + "data": "\u2029", + "valid": false + }, + { + "description": "EM SPACE does not match (Space_Separator)", + "data": "\u2003", + "valid": false + }, + { + "description": "Non-whitespace control matches", + "data": "\u0001", + "valid": true + }, + { + "description": "Non-whitespace matches", + "data": "\u2013", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/float-overflow.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/float-overflow.json new file mode 100755 index 00000000..52ff9827 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/float-overflow.json @@ -0,0 +1,13 @@ +[ + { + "description": "all integers are multiples of 0.5, if overflow is handled", + "schema": {"type": "integer", "multipleOf": 0.5}, + "tests": [ + { + "description": "valid if optional overflow handling is implemented", + "data": 1e308, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format.json deleted file mode 100644 index 6eafec8c..00000000 --- a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format.json +++ /dev/null @@ -1,133 +0,0 @@ -[ - { - "description": "validation of date-time strings", - "schema": {"format": "date-time"}, - "tests": [ - { - "description": "a valid date-time string", - "data": "1963-06-19T08:30:06.283185Z", - "valid": true - }, - { - "description": "an invalid date-time string", - "data": "06/19/1963 08:30:06 PST", - "valid": false - }, - { - "description": "only RFC3339 not all of ISO 8601 are valid", - "data": "2013-350T01:01:01", - "valid": false - } - ] - }, - { - "description": "validation of URIs", - "schema": {"format": "uri"}, - "tests": [ - { - "description": "a valid URI", - "data": "http://foo.bar/?baz=qux#quux", - "valid": true - }, - { - "description": "an now valid URI", - "data": "\\\\WINDOWS\\fileshare", - "valid": true - }, - { - "description": "a valid URI path part may not be //", - "data": "http://ibm.com//goo", - "valid": false - } - ] - }, - { - "description": "validation of e-mail addresses", - "schema": {"format": "email"}, - "tests": [ - { - "description": "a valid e-mail address", - "data": "joe.bloggs@example.com", - "valid": true - }, - { - "description": "an invalid e-mail address", - "data": "2962", - "valid": false - } - ] - }, - { - "description": "validation of IP addresses", - "schema": {"format": "ipv4"}, - "tests": [ - { - "description": "a valid IP address", - "data": "192.168.0.1", - "valid": true - }, - { - "description": "an IP address with too many components", - "data": "127.0.0.0.1", - "valid": false - }, - { - "description": "an IP address with out-of-range values", - "data": "256.256.256.256", - "valid": false - } - ] - }, - { - "description": "validation of IPv6 addresses", - "schema": {"format": "ipv6"}, - "tests": [ - { - "description": "a valid IPv6 address", - "data": "::1", - "valid": true - }, - { - "description": "an IPv6 address with out-of-range values", - "data": "12345::", - "valid": false - }, - { - "description": "an IPv6 address with too many components", - "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", - "valid": false - }, - { - "description": "an IPv6 address containing illegal characters", - "data": "::laptop", - "valid": false - } - ] - }, - { - "description": "validation of host names", - "schema": {"format": "hostname"}, - "tests": [ - { - "description": "a valid host name", - "data": "www.example.com", - "valid": true - }, - { - "description": "a host name starting with an illegal character", - "data": "-a-host-name-that-starts-with--", - "valid": false - }, - { - "description": "a host name containing illegal characters", - "data": "not_a_valid_host_name", - "valid": false - }, - { - "description": "a host name with a component too long", - "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", - "valid": false - } - ] - } -] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/date-time.json new file mode 100755 index 00000000..900fcb7c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/date-time.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "a valid date-time string without second fraction", + "data": "1963-06-19T08:30:06Z", + "valid": true + }, + { + "description": "a valid date-time string with plus offset", + "data": "1937-01-01T12:00:27.87+00:20", + "valid": true + }, + { + "description": "a valid date-time string with minus offset", + "data": "1990-12-31T15:59:50.123-08:00", + "valid": true + }, + { + "description": "a invalid day in date-time string", + "data": "1990-02-31T15:59:60.123-08:00", + "valid": false + }, + { + "description": "an invalid offset in date-time string", + "data": "1990-12-31T15:59:60-24:00", + "valid": false + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/hostname.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/hostname.json new file mode 100755 index 00000000..e913abad --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/hostname.json @@ -0,0 +1,64 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] + diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv4.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv4.json new file mode 100755 index 00000000..8b99b9fb --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv4.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + }, + { + "description": "an IP address as an integer (decimal)", + "data": "2130706433", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv6.json new file mode 100755 index 00000000..2a08cb46 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/ipv6.json @@ -0,0 +1,153 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::42:ff:1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "missing leading octet is invalid", + "data": ":2:3:4:5:6:7:8", + "valid": false + }, + { + "description": "missing trailing octet is invalid", + "data": "1:2:3:4:5:6:7:", + "valid": false + }, + { + "description": "missing leading octet with omitted octets later", + "data": ":2:3:4::8", + "valid": false + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + }, + { + "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)", + "data": "::ffff:192.168.0.1", + "valid": true + }, + { + "description": "triple colons is invalid", + "data": "1:2:3:4:5:::8", + "valid": false + }, + { + "description": "8 octets", + "data": "1:2:3:4:5:6:7:8", + "valid": true + }, + { + "description": "insufficient octets without double colons", + "data": "1:2:3:4:5:6:7", + "valid": false + }, + { + "description": "no colons is invalid", + "data": "1", + "valid": false + }, + { + "description": "ipv4 is not ipv6", + "data": "127.0.0.1", + "valid": false + }, + { + "description": "ipv4 segment must have 4 octets", + "data": "1:2:3:4:1.2.3", + "valid": false + }, + { + "description": "leading whitespace is invalid", + "data": " ::1", + "valid": false + }, + { + "description": "trailing whitespace is invalid", + "data": "::1 ", + "valid": false + }, + { + "description": "netmask is not a part of ipv6 address", + "data": "fe80::/64", + "valid": false + }, + { + "description": "zone id is not a part of ipv6 address", + "data": "fe80::a%eth1", + "valid": false + }, + { + "description": "a long valid ipv6", + "data": "1000:1000:1000:1000:1000:1000:255.255.255.255", + "valid": true + }, + { + "description": "a long invalid ipv6, below length limit, first", + "data": "100:100:100:100:100:100:255.255.255.255.255", + "valid": false + }, + { + "description": "a long invalid ipv6, below length limit, second", + "data": "100:100:100:100:100:100:100:255.255.255.255", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/uri.json new file mode 100755 index 00000000..4306a686 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/format/uri.json @@ -0,0 +1,108 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + }, + { + "description": "an invalid URI with comma in scheme", + "data": "bar,baz:foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft4/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.json b/test/JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.json old mode 100644 new mode 100755 diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/pattern.json b/test/JSON-Schema-Test-Suite/tests/draft4/pattern.json old mode 100644 new mode 100755 index befc4b56..92db0f97 --- a/test/JSON-Schema-Test-Suite/tests/draft4/pattern.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/pattern.json @@ -14,9 +14,45 @@ "valid": false }, { - "description": "ignores non-strings", + "description": "ignores booleans", "data": true, "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/draft4/patternProperties.json old mode 100644 new mode 100755 index 18586e5d..5f741dfc --- a/test/JSON-Schema-Test-Suite/tests/draft4/patternProperties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/patternProperties.json @@ -29,7 +29,17 @@ "valid": false }, { - "description": "ignores non-objects", + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", "data": 12, "valid": true } diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/properties.json b/test/JSON-Schema-Test-Suite/tests/draft4/properties.json old mode 100644 new mode 100755 index cd1644dc..688527bc --- a/test/JSON-Schema-Test-Suite/tests/draft4/properties.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/properties.json @@ -29,9 +29,14 @@ "valid": true }, { - "description": "ignores non-objects", + "description": "ignores arrays", "data": [], "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true } ] }, @@ -88,5 +93,44 @@ "valid": false } ] + }, + { + "description": "properties with escaped characters", + "schema": { + "properties": { + "foo\nbar": {"type": "number"}, + "foo\"bar": {"type": "number"}, + "foo\\bar": {"type": "number"}, + "foo\rbar": {"type": "number"}, + "foo\tbar": {"type": "number"}, + "foo\fbar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with all numbers is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1", + "foo\\bar": "1", + "foo\rbar": "1", + "foo\tbar": "1", + "foo\fbar": "1" + }, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/ref.json b/test/JSON-Schema-Test-Suite/tests/draft4/ref.json old mode 100644 new mode 100755 index b38ff031..820839d7 --- a/test/JSON-Schema-Test-Suite/tests/draft4/ref.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/ref.json @@ -75,30 +75,47 @@ { "description": "escaped pointer ref", "schema": { - "tilda~field": {"type": "integer"}, - "slash/field": {"type": "integer"}, - "percent%field": {"type": "integer"}, + "definitions": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, "properties": { - "tilda": {"$ref": "#/tilda~0field"}, - "slash": {"$ref": "#/slash~1field"}, - "percent": {"$ref": "#/percent%25field"} + "tilde": {"$ref": "#/definitions/tilde~0field"}, + "slash": {"$ref": "#/definitions/slash~1field"}, + "percent": {"$ref": "#/definitions/percent%25field"} } }, "tests": [ { - "description": "slash", + "description": "slash invalid", "data": {"slash": "aoeu"}, "valid": false }, { - "description": "tilda", - "data": {"tilda": "aoeu"}, + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, "valid": false }, { - "description": "percent", + "description": "percent invalid", "data": {"percent": "aoeu"}, "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true } ] }, @@ -125,6 +142,39 @@ } ] }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, { "description": "remote ref, containing refs itself", "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, @@ -140,5 +190,272 @@ "valid": false } ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/definitions/is-string"} + }, + "definitions": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + }, + { + "description": "refs with quote", + "schema": { + "properties": { + "foo\"bar": {"$ref": "#/definitions/foo%22bar"} + }, + "definitions": { + "foo\"bar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with numbers is valid", + "data": { + "foo\"bar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\"bar": "1" + }, + "valid": false + } + ] + }, + { + "description": "Location-independent identifier", + "schema": { + "allOf": [{ + "$ref": "#foo" + }], + "definitions": { + "A": { + "id": "#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "allOf": [{ + "$ref": "http://localhost:1234/bar#foo" + }], + "definitions": { + "A": { + "id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "id": "http://localhost:1234/root", + "allOf": [{ + "$ref": "http://localhost:1234/nested.json#foo" + }], + "definitions": { + "A": { + "id": "nested.json", + "definitions": { + "B": { + "id": "#foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "definitions": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/definitions/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/definitions/a_string" }, + "valid": true + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/refRemote.json b/test/JSON-Schema-Test-Suite/tests/draft4/refRemote.json old mode 100644 new mode 100755 index 4ca80473..ce5e99a3 --- a/test/JSON-Schema-Test-Suite/tests/draft4/refRemote.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/refRemote.json @@ -50,25 +50,122 @@ ] }, { - "description": "change resolution scope", + "description": "base URI change", "schema": { "id": "http://localhost:1234/", "items": { - "id": "folder/", + "id": "baseUriChange/", "items": {"$ref": "folderInteger.json"} } }, "tests": [ { - "description": "changed scope ref valid", + "description": "base URI change ref valid", "data": [[1]], "valid": true }, { - "description": "changed scope ref invalid", + "description": "base URI change ref invalid", "data": [["a"]], "valid": false } ] + }, + { + "description": "base URI change - change folder", + "schema": { + "id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "id": "baseUriChangeFolder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "id": "baseUriChangeFolderInSubschema/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/required.json b/test/JSON-Schema-Test-Suite/tests/draft4/required.json old mode 100644 new mode 100755 index 612f73f3..9b05318f --- a/test/JSON-Schema-Test-Suite/tests/draft4/required.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/required.json @@ -18,6 +18,21 @@ "description": "non-present required property is invalid", "data": {"bar": 1}, "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true } ] }, @@ -35,5 +50,40 @@ "valid": true } ] + }, + { + "description": "required with escaped characters", + "schema": { + "required": [ + "foo\nbar", + "foo\"bar", + "foo\\bar", + "foo\rbar", + "foo\tbar", + "foo\fbar" + ] + }, + "tests": [ + { + "description": "object with all properties present is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with some properties missing is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1" + }, + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/type.json b/test/JSON-Schema-Test-Suite/tests/draft4/type.json old mode 100644 new mode 100755 index 257f0512..df46677b --- a/test/JSON-Schema-Test-Suite/tests/draft4/type.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/type.json @@ -18,6 +18,11 @@ "data": "foo", "valid": false }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, { "description": "an object is not an integer", "data": {}, @@ -49,6 +54,11 @@ "data": 1, "valid": true }, + { + "description": "a float with zero fractional part is a number", + "data": 1.0, + "valid": true + }, { "description": "a float is a number", "data": 1.1, @@ -59,6 +69,11 @@ "data": "foo", "valid": false }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, { "description": "an object is not a number", "data": {}, @@ -100,6 +115,16 @@ "data": "foo", "valid": true }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an empty string is still a string", + "data": "", + "valid": true + }, { "description": "an object is not a string", "data": {}, @@ -188,7 +213,7 @@ "valid": false }, { - "description": "an array is not an array", + "description": "an array is an array", "data": [], "valid": true }, @@ -213,6 +238,11 @@ "data": 1, "valid": false }, + { + "description": "zero is not a boolean", + "data": 0, + "valid": false + }, { "description": "a float is not a boolean", "data": 1.1, @@ -223,6 +253,11 @@ "data": "foo", "valid": false }, + { + "description": "an empty string is not a boolean", + "data": "", + "valid": false + }, { "description": "an object is not a boolean", "data": {}, @@ -234,10 +269,15 @@ "valid": false }, { - "description": "a boolean is not a boolean", + "description": "true is a boolean", "data": true, "valid": true }, + { + "description": "false is a boolean", + "data": false, + "valid": true + }, { "description": "null is not a boolean", "data": null, @@ -259,11 +299,21 @@ "data": 1.1, "valid": false }, + { + "description": "zero is not null", + "data": 0, + "valid": false + }, { "description": "a string is not null", "data": "foo", "valid": false }, + { + "description": "an empty string is not null", + "data": "", + "valid": false + }, { "description": "an object is not null", "data": {}, @@ -275,10 +325,15 @@ "valid": false }, { - "description": "a boolean is not null", + "description": "true is not null", "data": true, "valid": false }, + { + "description": "false is not null", + "data": false, + "valid": false + }, { "description": "null is null", "data": null, @@ -326,5 +381,89 @@ "valid": false } ] + }, + { + "description": "type as array with one item", + "schema": { + "type": ["string"] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "type: array or object", + "schema": { + "type": ["array", "object"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type: array, object or null", + "schema": { + "type": ["array", "object", "null"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] } ] diff --git a/test/JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json old mode 100644 new mode 100755 index c1f4ab99..4846c773 --- a/test/JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json +++ b/test/JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json @@ -18,6 +18,16 @@ "data": [1.0, 1.00, 1], "valid": false }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, { "description": "unique array of objects is valid", "data": [{"foo": "bar"}, {"foo": "baz"}], @@ -64,15 +74,310 @@ "data": [0, false], "valid": true }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, { "description": "unique heterogeneous types are valid", - "data": [{}, [1], true, null, 1], + "data": [{}, [1], true, null, 1, "{}"], "valid": true }, { "description": "non-unique heterogeneous types are invalid", "data": [{}, [1], true, null, {}, 1], "valid": false + }, + { + "description": "different objects are unique", + "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], + "valid": true + }, + { + "description": "objects are non-unique despite key order", + "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], + "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false } ] } diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/draft6/additionalItems.json new file mode 100755 index 00000000..ee46b61a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/additionalItems.json @@ -0,0 +1,130 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, valid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, null ], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, invalid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" }, { "type": "string" } ] } + ], + "items": [ {"type": "integer" } ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, "hello" ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/draft6/additionalProperties.json new file mode 100755 index 00000000..381275a5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/additionalProperties.json @@ -0,0 +1,133 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "allOf": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in allOf are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/allOf.json b/test/JSON-Schema-Test-Suite/tests/draft6/allOf.json new file mode 100755 index 00000000..ec9319e1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/allOf.json @@ -0,0 +1,294 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with one empty schema", + "schema": { + "allOf": [ + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with two empty schemas", + "schema": { + "allOf": [ + {}, + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with the first empty schema", + "schema": { + "allOf": [ + {}, + { "type": "number" } + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with the last empty schema", + "schema": { + "allOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "nested allOf, to check validation semantics", + "schema": { + "allOf": [ + { + "allOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "allOf combined with anyOf, oneOf", + "schema": { + "allOf": [ { "multipleOf": 2 } ], + "anyOf": [ { "multipleOf": 3 } ], + "oneOf": [ { "multipleOf": 5 } ] + }, + "tests": [ + { + "description": "allOf: false, anyOf: false, oneOf: false", + "data": 1, + "valid": false + }, + { + "description": "allOf: false, anyOf: false, oneOf: true", + "data": 5, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: false", + "data": 3, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: true", + "data": 15, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: false", + "data": 2, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: true", + "data": 10, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: false", + "data": 6, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: true", + "data": 30, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/anyOf.json b/test/JSON-Schema-Test-Suite/tests/draft6/anyOf.json new file mode 100755 index 00000000..b720afa8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/anyOf.json @@ -0,0 +1,215 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "anyOf with one empty schema", + "schema": { + "anyOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is valid", + "data": 123, + "valid": true + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/boolean_schema.json b/test/JSON-Schema-Test-Suite/tests/draft6/boolean_schema.json new file mode 100755 index 00000000..6d40f23f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/const.json b/test/JSON-Schema-Test-Suite/tests/draft6/const.json new file mode 100755 index 00000000..1c2cafcc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/const.json @@ -0,0 +1,342 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + }, + { + "description": "const with false does not match 0", + "schema": {"const": false}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "const with true does not match 1", + "schema": {"const": true}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "const with [false] does not match [0]", + "schema": {"const": [false]}, + "tests": [ + { + "description": "[false] is valid", + "data": [false], + "valid": true + }, + { + "description": "[0] is invalid", + "data": [0], + "valid": false + }, + { + "description": "[0.0] is invalid", + "data": [0.0], + "valid": false + } + ] + }, + { + "description": "const with [true] does not match [1]", + "schema": {"const": [true]}, + "tests": [ + { + "description": "[true] is valid", + "data": [true], + "valid": true + }, + { + "description": "[1] is invalid", + "data": [1], + "valid": false + }, + { + "description": "[1.0] is invalid", + "data": [1.0], + "valid": false + } + ] + }, + { + "description": "const with {\"a\": false} does not match {\"a\": 0}", + "schema": {"const": {"a": false}}, + "tests": [ + { + "description": "{\"a\": false} is valid", + "data": {"a": false}, + "valid": true + }, + { + "description": "{\"a\": 0} is invalid", + "data": {"a": 0}, + "valid": false + }, + { + "description": "{\"a\": 0.0} is invalid", + "data": {"a": 0.0}, + "valid": false + } + ] + }, + { + "description": "const with {\"a\": true} does not match {\"a\": 1}", + "schema": {"const": {"a": true}}, + "tests": [ + { + "description": "{\"a\": true} is valid", + "data": {"a": true}, + "valid": true + }, + { + "description": "{\"a\": 1} is invalid", + "data": {"a": 1}, + "valid": false + }, + { + "description": "{\"a\": 1.0} is invalid", + "data": {"a": 1.0}, + "valid": false + } + ] + }, + { + "description": "const with 0 does not match other zero-like types", + "schema": {"const": 0}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "empty string is invalid", + "data": "", + "valid": false + } + ] + }, + { + "description": "const with 1 does not match true", + "schema": {"const": 1}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "const with -2.0 matches integer and float types", + "schema": {"const": -2.0}, + "tests": [ + { + "description": "integer -2 is valid", + "data": -2, + "valid": true + }, + { + "description": "integer 2 is invalid", + "data": 2, + "valid": false + }, + { + "description": "float -2.0 is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float 2.0 is invalid", + "data": 2.0, + "valid": false + }, + { + "description": "float -2.00001 is invalid", + "data": -2.00001, + "valid": false + } + ] + }, + { + "description": "float and integers are equal up to 64-bit representation limits", + "schema": {"const": 9007199254740992}, + "tests": [ + { + "description": "integer is valid", + "data": 9007199254740992, + "valid": true + }, + { + "description": "integer minus one is invalid", + "data": 9007199254740991, + "valid": false + }, + { + "description": "float is valid", + "data": 9007199254740992.0, + "valid": true + }, + { + "description": "float minus one is invalid", + "data": 9007199254740991.0, + "valid": false + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "const": "hello\u0000there" }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/contains.json b/test/JSON-Schema-Test-Suite/tests/draft6/contains.json new file mode 100755 index 00000000..c5471cc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/contains.json @@ -0,0 +1,129 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true + } + ] + }, + { + "description": "items + contains", + "schema": { + "items": { "multipleOf": 2 }, + "contains": { "multipleOf": 3 } + }, + "tests": [ + { + "description": "matches items, does not match contains", + "data": [ 2, 4, 8 ], + "valid": false + }, + { + "description": "does not match items, matches contains", + "data": [ 3, 6, 9 ], + "valid": false + }, + { + "description": "matches both items and contains", + "data": [ 6, 12 ], + "valid": true + }, + { + "description": "matches neither items nor contains", + "data": [ 1, 5 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/default.json b/test/JSON-Schema-Test-Suite/tests/draft6/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/definitions.json b/test/JSON-Schema-Test-Suite/tests/draft6/definitions.json new file mode 100755 index 00000000..7f3b8997 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/definitions.json @@ -0,0 +1,32 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "valid definition schema", + "data": { + "definitions": { + "foo": {"type": "integer"} + } + }, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "invalid definition schema", + "data": { + "definitions": { + "foo": {"type": 1} + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/dependencies.json b/test/JSON-Schema-Test-Suite/tests/draft6/dependencies.json new file mode 100755 index 00000000..a5e54282 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/dependencies.json @@ -0,0 +1,248 @@ +[ + { + "description": "dependencies", + "schema": { + "dependencies": {"bar": ["foo"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "dependencies with empty array", + "schema": { + "dependencies": {"bar": []} + }, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "non-object is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "multiple dependencies", + "schema": { + "dependencies": {"quux": ["foo", "bar"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "multiple dependencies subschema", + "schema": { + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "dependencies with boolean subschemas", + "schema": { + "dependencies": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependencies": { + "foo\nbar": ["foo\rbar"], + "foo\tbar": { + "minProperties": 4 + }, + "foo'bar": {"required": ["foo\"bar"]}, + "foo\"bar": ["foo'bar"] + } + }, + "tests": [ + { + "description": "valid object 1", + "data": { + "foo\nbar": 1, + "foo\rbar": 2 + }, + "valid": true + }, + { + "description": "valid object 2", + "data": { + "foo\tbar": 1, + "a": 2, + "b": 3, + "c": 4 + }, + "valid": true + }, + { + "description": "valid object 3", + "data": { + "foo'bar": 1, + "foo\"bar": 2 + }, + "valid": true + }, + { + "description": "invalid object 1", + "data": { + "foo\nbar": 1, + "foo": 2 + }, + "valid": false + }, + { + "description": "invalid object 2", + "data": { + "foo\tbar": 1, + "a": 2 + }, + "valid": false + }, + { + "description": "invalid object 3", + "data": { + "foo'bar": 1 + }, + "valid": false + }, + { + "description": "invalid object 4", + "data": { + "foo\"bar": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/enum.json b/test/JSON-Schema-Test-Suite/tests/draft6/enum.json new file mode 100755 index 00000000..f085097b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/enum.json @@ -0,0 +1,236 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + }, + { + "description": "valid object matches", + "data": {"foo": 12}, + "valid": true + }, + { + "description": "extra properties in object is invalid", + "data": {"foo": 12, "boo": 42}, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "enum with escaped characters", + "schema": { + "enum": ["foo\nbar", "foo\rbar"] + }, + "tests": [ + { + "description": "member 1 is valid", + "data": "foo\nbar", + "valid": true + }, + { + "description": "member 2 is valid", + "data": "foo\rbar", + "valid": true + }, + { + "description": "another string is invalid", + "data": "abc", + "valid": false + } + ] + }, + { + "description": "enum with false does not match 0", + "schema": {"enum": [false]}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "enum with true does not match 1", + "schema": {"enum": [true]}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "enum with 0 does not match false", + "schema": {"enum": [0]}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + } + ] + }, + { + "description": "enum with 1 does not match true", + "schema": {"enum": [1]}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.json b/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.json new file mode 100755 index 00000000..dc3cd709 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.json b/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.json new file mode 100755 index 00000000..b38d7ece --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/format.json b/test/JSON-Schema-Test-Suite/tests/draft6/format.json new file mode 100755 index 00000000..32e81524 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/format.json @@ -0,0 +1,326 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/draft6/infinite-loop-detection.json new file mode 100755 index 00000000..f98c74fc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/infinite-loop-detection.json @@ -0,0 +1,36 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "definitions": { + "int": { "type": "integer" } + }, + "allOf": [ + { + "properties": { + "foo": { + "$ref": "#/definitions/int" + } + } + }, + { + "additionalProperties": { + "$ref": "#/definitions/int" + } + } + ] + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/items.json b/test/JSON-Schema-Test-Suite/tests/draft6/items.json new file mode 100755 index 00000000..67f11840 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/items.json @@ -0,0 +1,250 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items and subitems", + "schema": { + "definitions": { + "item": { + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/sub-item" }, + { "$ref": "#/definitions/sub-item" } + ] + }, + "sub-item": { + "type": "object", + "required": ["foo"] + } + }, + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" } + ] + }, + "tests": [ + { + "description": "valid items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": true + }, + { + "description": "too many items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "too many sub-items", + "data": [ + [ {"foo": null}, {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong item", + "data": [ + {"foo": null}, + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong sub-item", + "data": [ + [ {}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "fewer items is valid", + "data": [ + [ {"foo": null} ], + [ {"foo": null} ] + ], + "valid": true + } + ] + }, + { + "description": "nested items", + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "tests": [ + { + "description": "valid nested array", + "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": true + }, + { + "description": "nested array with invalid type", + "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": false + }, + { + "description": "not deep enough", + "data": [[[1], [2],[3]], [[4], [5], [6]]], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/maxItems.json b/test/JSON-Schema-Test-Suite/tests/draft6/maxItems.json new file mode 100755 index 00000000..3b53a6b3 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/maxLength.json b/test/JSON-Schema-Test-Suite/tests/draft6/maxLength.json new file mode 100755 index 00000000..811d35b2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/maxProperties.json b/test/JSON-Schema-Test-Suite/tests/draft6/maxProperties.json new file mode 100755 index 00000000..aa7209f5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/maxProperties.json @@ -0,0 +1,54 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "maxProperties = 0 means the object is empty", + "schema": { "maxProperties": 0 }, + "tests": [ + { + "description": "no properties is valid", + "data": {}, + "valid": true + }, + { + "description": "one property is invalid", + "data": { "foo": 1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/maximum.json b/test/JSON-Schema-Test-Suite/tests/draft6/maximum.json new file mode 100755 index 00000000..6844a39e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/maximum.json @@ -0,0 +1,54 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/minItems.json b/test/JSON-Schema-Test-Suite/tests/draft6/minItems.json new file mode 100755 index 00000000..ed511881 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/minLength.json b/test/JSON-Schema-Test-Suite/tests/draft6/minLength.json new file mode 100755 index 00000000..3f09158d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/minProperties.json b/test/JSON-Schema-Test-Suite/tests/draft6/minProperties.json new file mode 100755 index 00000000..49a0726e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/minimum.json b/test/JSON-Schema-Test-Suite/tests/draft6/minimum.json new file mode 100755 index 00000000..21ae50e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/minimum.json @@ -0,0 +1,69 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/multipleOf.json b/test/JSON-Schema-Test-Suite/tests/draft6/multipleOf.json new file mode 100755 index 00000000..faa87cff --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/multipleOf.json @@ -0,0 +1,71 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + }, + { + "description": "invalid instance should not raise error when float division = inf", + "schema": {"type": "integer", "multipleOf": 0.123456789}, + "tests": [ + { + "description": "always invalid, but naive implementations may raise an overflow error", + "data": 1e308, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/not.json b/test/JSON-Schema-Test-Suite/tests/draft6/not.json new file mode 100755 index 00000000..98de0eda --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/oneOf.json b/test/JSON-Schema-Test-Suite/tests/draft6/oneOf.json new file mode 100755 index 00000000..eeb7ae86 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/oneOf.json @@ -0,0 +1,274 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "oneOf with empty schema", + "schema": { + "oneOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "one valid - valid", + "data": "foo", + "valid": true + }, + { + "description": "both valid - invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "oneOf with required", + "schema": { + "type": "object", + "oneOf": [ + { "required": ["foo", "bar"] }, + { "required": ["foo", "baz"] } + ] + }, + "tests": [ + { + "description": "both invalid - invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "first valid - valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "second valid - valid", + "data": {"foo": 1, "baz": 3}, + "valid": true + }, + { + "description": "both valid - invalid", + "data": {"foo": 1, "bar": 2, "baz" : 3}, + "valid": false + } + ] + }, + { + "description": "oneOf with missing optional property", + "schema": { + "oneOf": [ + { + "properties": { + "bar": true, + "baz": true + }, + "required": ["bar"] + }, + { + "properties": { + "foo": true + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": {"bar": 8}, + "valid": true + }, + { + "description": "second oneOf valid", + "data": {"foo": "foo"}, + "valid": true + }, + { + "description": "both oneOf valid", + "data": {"foo": "foo", "bar": 8}, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": {"baz": "quux"}, + "valid": false + } + ] + }, + { + "description": "nested oneOf, to check validation semantics", + "schema": { + "oneOf": [ + { + "oneOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/bignum.json new file mode 100755 index 00000000..fac275e2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.json new file mode 100755 index 00000000..6ed6cbe4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.json @@ -0,0 +1,292 @@ +[ + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\t to horizontal tab", + "schema": { + "type": "string", + "pattern": "^\\t$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\t", + "valid": false + }, + { + "description": "matches", + "data": "\u0009", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\W matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches whitespace", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "Character tabulation matches", + "data": "\t", + "valid": true + }, + { + "description": "Line tabulation matches", + "data": "\u000b", + "valid": true + }, + { + "description": "Form feed matches", + "data": "\u000c", + "valid": true + }, + { + "description": "latin-1 non-breaking-space matches", + "data": "\u00a0", + "valid": true + }, + { + "description": "zero-width whitespace matches", + "data": "\ufeff", + "valid": true + }, + { + "description": "line feed matches (line terminator)", + "data": "\u000a", + "valid": true + }, + { + "description": "paragraph separator matches (line terminator)", + "data": "\u2029", + "valid": true + }, + { + "description": "EM SPACE matches (Space_Separator)", + "data": "\u2003", + "valid": true + }, + { + "description": "Non-whitespace control does not match", + "data": "\u0001", + "valid": false + }, + { + "description": "Non-whitespace does not match", + "data": "\u2013", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "Character tabulation does not match", + "data": "\t", + "valid": false + }, + { + "description": "Line tabulation does not match", + "data": "\u000b", + "valid": false + }, + { + "description": "Form feed does not match", + "data": "\u000c", + "valid": false + }, + { + "description": "latin-1 non-breaking-space does not match", + "data": "\u00a0", + "valid": false + }, + { + "description": "zero-width whitespace does not match", + "data": "\ufeff", + "valid": false + }, + { + "description": "line feed does not match (line terminator)", + "data": "\u000a", + "valid": false + }, + { + "description": "paragraph separator does not match (line terminator)", + "data": "\u2029", + "valid": false + }, + { + "description": "EM SPACE does not match (Space_Separator)", + "data": "\u2003", + "valid": false + }, + { + "description": "Non-whitespace control matches", + "data": "\u0001", + "valid": true + }, + { + "description": "Non-whitespace matches", + "data": "\u2013", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/float-overflow.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/float-overflow.json new file mode 100755 index 00000000..52ff9827 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/float-overflow.json @@ -0,0 +1,13 @@ +[ + { + "description": "all integers are multiples of 0.5, if overflow is handled", + "schema": {"type": "integer", "multipleOf": 0.5}, + "tests": [ + { + "description": "valid if optional overflow handling is implemented", + "data": 1e308, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/date-time.json new file mode 100755 index 00000000..a6320a90 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/date-time.json @@ -0,0 +1,68 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "a valid date-time string without second fraction", + "data": "1963-06-19T08:30:06Z", + "valid": true + }, + { + "description": "a valid date-time string with plus offset", + "data": "1937-01-01T12:00:27.87+00:20", + "valid": true + }, + { + "description": "a valid date-time string with minus offset", + "data": "1990-12-31T15:59:50.123-08:00", + "valid": true + }, + { + "description": "a invalid day in date-time string", + "data": "1990-02-31T15:59:60.123-08:00", + "valid": false + }, + { + "description": "an invalid offset in date-time string", + "data": "1990-12-31T15:59:60-24:00", + "valid": false + }, + { + "description": "an invalid closing Z after time-zone offset", + "data": "1963-06-19T08:30:06.28123+01:00Z", + "valid": false + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/hostname.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/hostname.json new file mode 100755 index 00000000..d7654e01 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/hostname.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv4.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv4.json new file mode 100755 index 00000000..8b99b9fb --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv4.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + }, + { + "description": "an IP address as an integer (decimal)", + "data": "2130706433", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv6.json new file mode 100755 index 00000000..2a08cb46 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/ipv6.json @@ -0,0 +1,153 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::42:ff:1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "missing leading octet is invalid", + "data": ":2:3:4:5:6:7:8", + "valid": false + }, + { + "description": "missing trailing octet is invalid", + "data": "1:2:3:4:5:6:7:", + "valid": false + }, + { + "description": "missing leading octet with omitted octets later", + "data": ":2:3:4::8", + "valid": false + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + }, + { + "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)", + "data": "::ffff:192.168.0.1", + "valid": true + }, + { + "description": "triple colons is invalid", + "data": "1:2:3:4:5:::8", + "valid": false + }, + { + "description": "8 octets", + "data": "1:2:3:4:5:6:7:8", + "valid": true + }, + { + "description": "insufficient octets without double colons", + "data": "1:2:3:4:5:6:7", + "valid": false + }, + { + "description": "no colons is invalid", + "data": "1", + "valid": false + }, + { + "description": "ipv4 is not ipv6", + "data": "127.0.0.1", + "valid": false + }, + { + "description": "ipv4 segment must have 4 octets", + "data": "1:2:3:4:1.2.3", + "valid": false + }, + { + "description": "leading whitespace is invalid", + "data": " ::1", + "valid": false + }, + { + "description": "trailing whitespace is invalid", + "data": "::1 ", + "valid": false + }, + { + "description": "netmask is not a part of ipv6 address", + "data": "fe80::/64", + "valid": false + }, + { + "description": "zone id is not a part of ipv6 address", + "data": "fe80::a%eth1", + "valid": false + }, + { + "description": "a long valid ipv6", + "data": "1000:1000:1000:1000:1000:1000:255.255.255.255", + "valid": true + }, + { + "description": "a long invalid ipv6, below length limit, first", + "data": "100:100:100:100:100:100:255.255.255.255.255", + "valid": false + }, + { + "description": "a long invalid ipv6, below length limit, second", + "data": "100:100:100:100:100:100:100:255.255.255.255", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/json-pointer.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/json-pointer.json new file mode 100755 index 00000000..65c2f064 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/json-pointer.json @@ -0,0 +1,168 @@ +[ + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-reference.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-reference.json new file mode 100755 index 00000000..e4c9eef6 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-template.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-template.json new file mode 100755 index 00000000..33ab76ee --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri-template.json @@ -0,0 +1,28 @@ +[ + { + "description": "format: uri-template", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri.json new file mode 100755 index 00000000..4306a686 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/format/uri.json @@ -0,0 +1,108 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + }, + { + "description": "an invalid URI with comma in scheme", + "data": "bar,baz:foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/draft6/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/pattern.json b/test/JSON-Schema-Test-Suite/tests/draft6/pattern.json new file mode 100755 index 00000000..92db0f97 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/pattern.json @@ -0,0 +1,59 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid", + "data": "abc", + "valid": false + }, + { + "description": "ignores booleans", + "data": true, + "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/draft6/patternProperties.json new file mode 100755 index 00000000..c10ffcc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/patternProperties.json @@ -0,0 +1,156 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "object with a property matching both true and false is invalid", + "data": {"foobar":1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/properties.json b/test/JSON-Schema-Test-Suite/tests/draft6/properties.json new file mode 100755 index 00000000..b86c1819 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/properties.json @@ -0,0 +1,167 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + }, + { + "description": "properties with escaped characters", + "schema": { + "properties": { + "foo\nbar": {"type": "number"}, + "foo\"bar": {"type": "number"}, + "foo\\bar": {"type": "number"}, + "foo\rbar": {"type": "number"}, + "foo\tbar": {"type": "number"}, + "foo\fbar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with all numbers is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1", + "foo\\bar": "1", + "foo\rbar": "1", + "foo\tbar": "1", + "foo\fbar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/propertyNames.json b/test/JSON-Schema-Test-Suite/tests/draft6/propertyNames.json new file mode 100755 index 00000000..8423690d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/ref.json b/test/JSON-Schema-Test-Suite/tests/draft6/ref.json new file mode 100755 index 00000000..3bb0efc3 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/ref.json @@ -0,0 +1,493 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "definitions": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, + "properties": { + "tilde": {"$ref": "#/definitions/tilde~0field"}, + "slash": {"$ref": "#/definitions/slash~1field"}, + "percent": {"$ref": "#/definitions/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/definitions/is-string"} + }, + "definitions": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + }, + { + "description": "refs with quote", + "schema": { + "properties": { + "foo\"bar": {"$ref": "#/definitions/foo%22bar"} + }, + "definitions": { + "foo\"bar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with numbers is valid", + "data": { + "foo\"bar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\"bar": "1" + }, + "valid": false + } + ] + }, + { + "description": "Location-independent identifier", + "schema": { + "allOf": [{ + "$ref": "#foo" + }], + "definitions": { + "A": { + "$id": "#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "allOf": [{ + "$ref": "http://localhost:1234/bar#foo" + }], + "definitions": { + "A": { + "$id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "$id": "http://localhost:1234/root", + "allOf": [{ + "$ref": "http://localhost:1234/nested.json#foo" + }], + "definitions": { + "A": { + "$id": "nested.json", + "definitions": { + "B": { + "$id": "#foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "definitions": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/definitions/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/definitions/a_string" }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/refRemote.json b/test/JSON-Schema-Test-Suite/tests/draft6/refRemote.json new file mode 100755 index 00000000..74a78626 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/refRemote.json @@ -0,0 +1,171 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "baseUriChange/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "$id": "baseUriChangeFolder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "$id": "baseUriChangeFolderInSubschema/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/required.json b/test/JSON-Schema-Test-Suite/tests/draft6/required.json new file mode 100755 index 00000000..abf18f34 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/required.json @@ -0,0 +1,105 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with escaped characters", + "schema": { + "required": [ + "foo\nbar", + "foo\"bar", + "foo\\bar", + "foo\rbar", + "foo\tbar", + "foo\fbar" + ] + }, + "tests": [ + { + "description": "object with all properties present is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with some properties missing is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/type.json b/test/JSON-Schema-Test-Suite/tests/draft6/type.json new file mode 100755 index 00000000..83046470 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/type.json @@ -0,0 +1,474 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is an integer", + "data": 1.0, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is a number (and an integer)", + "data": 1.0, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an empty string is still a string", + "data": "", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "zero is not a boolean", + "data": 0, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not a boolean", + "data": "", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "true is a boolean", + "data": true, + "valid": true + }, + { + "description": "false is a boolean", + "data": false, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "zero is not null", + "data": 0, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not null", + "data": "", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "true is not null", + "data": true, + "valid": false + }, + { + "description": "false is not null", + "data": false, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type as array with one item", + "schema": { + "type": ["string"] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "type: array or object", + "schema": { + "type": ["array", "object"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type: array, object or null", + "schema": { + "type": ["array", "object", "null"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft6/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/draft6/uniqueItems.json new file mode 100755 index 00000000..4846c773 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft6/uniqueItems.json @@ -0,0 +1,384 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1, "{}"], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + }, + { + "description": "different objects are unique", + "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], + "valid": true + }, + { + "description": "objects are non-unique despite key order", + "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], + "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json new file mode 100755 index 00000000..ee46b61a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/additionalItems.json @@ -0,0 +1,130 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, valid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, null ], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, invalid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" }, { "type": "string" } ] } + ], + "items": [ {"type": "integer" } ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, "hello" ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json new file mode 100755 index 00000000..381275a5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/additionalProperties.json @@ -0,0 +1,133 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "allOf": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in allOf are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/allOf.json b/test/JSON-Schema-Test-Suite/tests/draft7/allOf.json new file mode 100755 index 00000000..ec9319e1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/allOf.json @@ -0,0 +1,294 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with one empty schema", + "schema": { + "allOf": [ + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with two empty schemas", + "schema": { + "allOf": [ + {}, + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with the first empty schema", + "schema": { + "allOf": [ + {}, + { "type": "number" } + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with the last empty schema", + "schema": { + "allOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "nested allOf, to check validation semantics", + "schema": { + "allOf": [ + { + "allOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "allOf combined with anyOf, oneOf", + "schema": { + "allOf": [ { "multipleOf": 2 } ], + "anyOf": [ { "multipleOf": 3 } ], + "oneOf": [ { "multipleOf": 5 } ] + }, + "tests": [ + { + "description": "allOf: false, anyOf: false, oneOf: false", + "data": 1, + "valid": false + }, + { + "description": "allOf: false, anyOf: false, oneOf: true", + "data": 5, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: false", + "data": 3, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: true", + "data": 15, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: false", + "data": 2, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: true", + "data": 10, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: false", + "data": 6, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: true", + "data": 30, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/anyOf.json b/test/JSON-Schema-Test-Suite/tests/draft7/anyOf.json new file mode 100755 index 00000000..b720afa8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/anyOf.json @@ -0,0 +1,215 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "anyOf with one empty schema", + "schema": { + "anyOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is valid", + "data": 123, + "valid": true + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json b/test/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json new file mode 100755 index 00000000..6d40f23f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/const.json b/test/JSON-Schema-Test-Suite/tests/draft7/const.json new file mode 100755 index 00000000..1c2cafcc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/const.json @@ -0,0 +1,342 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + }, + { + "description": "const with false does not match 0", + "schema": {"const": false}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "const with true does not match 1", + "schema": {"const": true}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "const with [false] does not match [0]", + "schema": {"const": [false]}, + "tests": [ + { + "description": "[false] is valid", + "data": [false], + "valid": true + }, + { + "description": "[0] is invalid", + "data": [0], + "valid": false + }, + { + "description": "[0.0] is invalid", + "data": [0.0], + "valid": false + } + ] + }, + { + "description": "const with [true] does not match [1]", + "schema": {"const": [true]}, + "tests": [ + { + "description": "[true] is valid", + "data": [true], + "valid": true + }, + { + "description": "[1] is invalid", + "data": [1], + "valid": false + }, + { + "description": "[1.0] is invalid", + "data": [1.0], + "valid": false + } + ] + }, + { + "description": "const with {\"a\": false} does not match {\"a\": 0}", + "schema": {"const": {"a": false}}, + "tests": [ + { + "description": "{\"a\": false} is valid", + "data": {"a": false}, + "valid": true + }, + { + "description": "{\"a\": 0} is invalid", + "data": {"a": 0}, + "valid": false + }, + { + "description": "{\"a\": 0.0} is invalid", + "data": {"a": 0.0}, + "valid": false + } + ] + }, + { + "description": "const with {\"a\": true} does not match {\"a\": 1}", + "schema": {"const": {"a": true}}, + "tests": [ + { + "description": "{\"a\": true} is valid", + "data": {"a": true}, + "valid": true + }, + { + "description": "{\"a\": 1} is invalid", + "data": {"a": 1}, + "valid": false + }, + { + "description": "{\"a\": 1.0} is invalid", + "data": {"a": 1.0}, + "valid": false + } + ] + }, + { + "description": "const with 0 does not match other zero-like types", + "schema": {"const": 0}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "empty string is invalid", + "data": "", + "valid": false + } + ] + }, + { + "description": "const with 1 does not match true", + "schema": {"const": 1}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "const with -2.0 matches integer and float types", + "schema": {"const": -2.0}, + "tests": [ + { + "description": "integer -2 is valid", + "data": -2, + "valid": true + }, + { + "description": "integer 2 is invalid", + "data": 2, + "valid": false + }, + { + "description": "float -2.0 is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float 2.0 is invalid", + "data": 2.0, + "valid": false + }, + { + "description": "float -2.00001 is invalid", + "data": -2.00001, + "valid": false + } + ] + }, + { + "description": "float and integers are equal up to 64-bit representation limits", + "schema": {"const": 9007199254740992}, + "tests": [ + { + "description": "integer is valid", + "data": 9007199254740992, + "valid": true + }, + { + "description": "integer minus one is invalid", + "data": 9007199254740991, + "valid": false + }, + { + "description": "float is valid", + "data": 9007199254740992.0, + "valid": true + }, + { + "description": "float minus one is invalid", + "data": 9007199254740991.0, + "valid": false + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "const": "hello\u0000there" }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/contains.json b/test/JSON-Schema-Test-Suite/tests/draft7/contains.json new file mode 100755 index 00000000..c5471cc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/contains.json @@ -0,0 +1,129 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true + } + ] + }, + { + "description": "items + contains", + "schema": { + "items": { "multipleOf": 2 }, + "contains": { "multipleOf": 3 } + }, + "tests": [ + { + "description": "matches items, does not match contains", + "data": [ 2, 4, 8 ], + "valid": false + }, + { + "description": "does not match items, matches contains", + "data": [ 3, 6, 9 ], + "valid": false + }, + { + "description": "matches both items and contains", + "data": [ 6, 12 ], + "valid": true + }, + { + "description": "matches neither items nor contains", + "data": [ 1, 5 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/default.json b/test/JSON-Schema-Test-Suite/tests/draft7/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/definitions.json b/test/JSON-Schema-Test-Suite/tests/draft7/definitions.json new file mode 100755 index 00000000..43604065 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/definitions.json @@ -0,0 +1,32 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "valid definition schema", + "data": { + "definitions": { + "foo": {"type": "integer"} + } + }, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "invalid definition schema", + "data": { + "definitions": { + "foo": {"type": 1} + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/dependencies.json b/test/JSON-Schema-Test-Suite/tests/draft7/dependencies.json new file mode 100755 index 00000000..a5e54282 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/dependencies.json @@ -0,0 +1,248 @@ +[ + { + "description": "dependencies", + "schema": { + "dependencies": {"bar": ["foo"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "dependencies with empty array", + "schema": { + "dependencies": {"bar": []} + }, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "non-object is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "multiple dependencies", + "schema": { + "dependencies": {"quux": ["foo", "bar"]} + }, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "multiple dependencies subschema", + "schema": { + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "dependencies with boolean subschemas", + "schema": { + "dependencies": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependencies": { + "foo\nbar": ["foo\rbar"], + "foo\tbar": { + "minProperties": 4 + }, + "foo'bar": {"required": ["foo\"bar"]}, + "foo\"bar": ["foo'bar"] + } + }, + "tests": [ + { + "description": "valid object 1", + "data": { + "foo\nbar": 1, + "foo\rbar": 2 + }, + "valid": true + }, + { + "description": "valid object 2", + "data": { + "foo\tbar": 1, + "a": 2, + "b": 3, + "c": 4 + }, + "valid": true + }, + { + "description": "valid object 3", + "data": { + "foo'bar": 1, + "foo\"bar": 2 + }, + "valid": true + }, + { + "description": "invalid object 1", + "data": { + "foo\nbar": 1, + "foo": 2 + }, + "valid": false + }, + { + "description": "invalid object 2", + "data": { + "foo\tbar": 1, + "a": 2 + }, + "valid": false + }, + { + "description": "invalid object 3", + "data": { + "foo'bar": 1 + }, + "valid": false + }, + { + "description": "invalid object 4", + "data": { + "foo\"bar": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/enum.json b/test/JSON-Schema-Test-Suite/tests/draft7/enum.json new file mode 100755 index 00000000..f085097b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/enum.json @@ -0,0 +1,236 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + }, + { + "description": "valid object matches", + "data": {"foo": 12}, + "valid": true + }, + { + "description": "extra properties in object is invalid", + "data": {"foo": 12, "boo": 42}, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "enum with escaped characters", + "schema": { + "enum": ["foo\nbar", "foo\rbar"] + }, + "tests": [ + { + "description": "member 1 is valid", + "data": "foo\nbar", + "valid": true + }, + { + "description": "member 2 is valid", + "data": "foo\rbar", + "valid": true + }, + { + "description": "another string is invalid", + "data": "abc", + "valid": false + } + ] + }, + { + "description": "enum with false does not match 0", + "schema": {"enum": [false]}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "enum with true does not match 1", + "schema": {"enum": [true]}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "enum with 0 does not match false", + "schema": {"enum": [0]}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + } + ] + }, + { + "description": "enum with 1 does not match true", + "schema": {"enum": [1]}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json b/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json new file mode 100755 index 00000000..dc3cd709 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json b/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json new file mode 100755 index 00000000..b38d7ece --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/format.json b/test/JSON-Schema-Test-Suite/tests/draft7/format.json new file mode 100755 index 00000000..93305f5c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/format.json @@ -0,0 +1,614 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/if-then-else.json b/test/JSON-Schema-Test-Suite/tests/draft7/if-then-else.json new file mode 100755 index 00000000..284e9191 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/if-then-else.json @@ -0,0 +1,258 @@ +[ + { + "description": "ignore if without then or else", + "schema": { + "if": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone if", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone if", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore then without if", + "schema": { + "then": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone then", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone then", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore else without if", + "schema": { + "else": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone else", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone else", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "if and then without else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid when if test fails", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if and else without then", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid when if test passes", + "data": -1, + "valid": true + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "validate against correct branch, then vs else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "non-interference across combined schemas", + "schema": { + "allOf": [ + { + "if": { + "exclusiveMaximum": 0 + } + }, + { + "then": { + "minimum": -10 + } + }, + { + "else": { + "multipleOf": 2 + } + } + ] + }, + "tests": [ + { + "description": "valid, but would have been invalid through then", + "data": -100, + "valid": true + }, + { + "description": "valid, but would have been invalid through else", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if with boolean schema true", + "schema": { + "if": true, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema true in if always chooses the then path (valid)", + "data": "then", + "valid": true + }, + { + "description": "boolean schema true in if always chooses the then path (invalid)", + "data": "else", + "valid": false + } + ] + }, + { + "description": "if with boolean schema false", + "schema": { + "if": false, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema false in if always chooses the else path (invalid)", + "data": "then", + "valid": false + }, + { + "description": "boolean schema false in if always chooses the else path (valid)", + "data": "else", + "valid": true + } + ] + }, + { + "description": "if appears at the end when serialized (keyword processing sequence)", + "schema": { + "then": { "const": "yes" }, + "else": { "const": "other" }, + "if": { "maxLength": 4 } + }, + "tests": [ + { + "description": "yes redirects to then and passes", + "data": "yes", + "valid": true + }, + { + "description": "other redirects to else and passes", + "data": "other", + "valid": true + }, + { + "description": "no redirects to then and fails", + "data": "no", + "valid": false + }, + { + "description": "invalid redirects to else and fails", + "data": "invalid", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/draft7/infinite-loop-detection.json new file mode 100755 index 00000000..f98c74fc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/infinite-loop-detection.json @@ -0,0 +1,36 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "definitions": { + "int": { "type": "integer" } + }, + "allOf": [ + { + "properties": { + "foo": { + "$ref": "#/definitions/int" + } + } + }, + { + "additionalProperties": { + "$ref": "#/definitions/int" + } + } + ] + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/items.json b/test/JSON-Schema-Test-Suite/tests/draft7/items.json new file mode 100755 index 00000000..67f11840 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/items.json @@ -0,0 +1,250 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items and subitems", + "schema": { + "definitions": { + "item": { + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/sub-item" }, + { "$ref": "#/definitions/sub-item" } + ] + }, + "sub-item": { + "type": "object", + "required": ["foo"] + } + }, + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" }, + { "$ref": "#/definitions/item" } + ] + }, + "tests": [ + { + "description": "valid items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": true + }, + { + "description": "too many items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "too many sub-items", + "data": [ + [ {"foo": null}, {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong item", + "data": [ + {"foo": null}, + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong sub-item", + "data": [ + [ {}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "fewer items is valid", + "data": [ + [ {"foo": null} ], + [ {"foo": null} ] + ], + "valid": true + } + ] + }, + { + "description": "nested items", + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "tests": [ + { + "description": "valid nested array", + "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": true + }, + { + "description": "nested array with invalid type", + "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": false + }, + { + "description": "not deep enough", + "data": [[[1], [2],[3]], [[4], [5], [6]]], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/maxItems.json b/test/JSON-Schema-Test-Suite/tests/draft7/maxItems.json new file mode 100755 index 00000000..3b53a6b3 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/maxLength.json b/test/JSON-Schema-Test-Suite/tests/draft7/maxLength.json new file mode 100755 index 00000000..811d35b2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json b/test/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json new file mode 100755 index 00000000..aa7209f5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/maxProperties.json @@ -0,0 +1,54 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "maxProperties = 0 means the object is empty", + "schema": { "maxProperties": 0 }, + "tests": [ + { + "description": "no properties is valid", + "data": {}, + "valid": true + }, + { + "description": "one property is invalid", + "data": { "foo": 1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/maximum.json b/test/JSON-Schema-Test-Suite/tests/draft7/maximum.json new file mode 100755 index 00000000..6844a39e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/maximum.json @@ -0,0 +1,54 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/minItems.json b/test/JSON-Schema-Test-Suite/tests/draft7/minItems.json new file mode 100755 index 00000000..ed511881 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/minLength.json b/test/JSON-Schema-Test-Suite/tests/draft7/minLength.json new file mode 100755 index 00000000..3f09158d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/minProperties.json b/test/JSON-Schema-Test-Suite/tests/draft7/minProperties.json new file mode 100755 index 00000000..49a0726e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/minimum.json b/test/JSON-Schema-Test-Suite/tests/draft7/minimum.json new file mode 100755 index 00000000..21ae50e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/minimum.json @@ -0,0 +1,69 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json b/test/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json new file mode 100755 index 00000000..faa87cff --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/multipleOf.json @@ -0,0 +1,71 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + }, + { + "description": "invalid instance should not raise error when float division = inf", + "schema": {"type": "integer", "multipleOf": 0.123456789}, + "tests": [ + { + "description": "always invalid, but naive implementations may raise an overflow error", + "data": 1e308, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/not.json b/test/JSON-Schema-Test-Suite/tests/draft7/not.json new file mode 100755 index 00000000..98de0eda --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/oneOf.json b/test/JSON-Schema-Test-Suite/tests/draft7/oneOf.json new file mode 100755 index 00000000..eeb7ae86 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/oneOf.json @@ -0,0 +1,274 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "oneOf with empty schema", + "schema": { + "oneOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "one valid - valid", + "data": "foo", + "valid": true + }, + { + "description": "both valid - invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "oneOf with required", + "schema": { + "type": "object", + "oneOf": [ + { "required": ["foo", "bar"] }, + { "required": ["foo", "baz"] } + ] + }, + "tests": [ + { + "description": "both invalid - invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "first valid - valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "second valid - valid", + "data": {"foo": 1, "baz": 3}, + "valid": true + }, + { + "description": "both valid - invalid", + "data": {"foo": 1, "bar": 2, "baz" : 3}, + "valid": false + } + ] + }, + { + "description": "oneOf with missing optional property", + "schema": { + "oneOf": [ + { + "properties": { + "bar": true, + "baz": true + }, + "required": ["bar"] + }, + { + "properties": { + "foo": true + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": {"bar": 8}, + "valid": true + }, + { + "description": "second oneOf valid", + "data": {"foo": "foo"}, + "valid": true + }, + { + "description": "both oneOf valid", + "data": {"foo": "foo", "bar": 8}, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": {"baz": "quux"}, + "valid": false + } + ] + }, + { + "description": "nested oneOf, to check validation semantics", + "schema": { + "oneOf": [ + { + "oneOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json new file mode 100755 index 00000000..fac275e2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/content.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/content.json new file mode 100755 index 00000000..3f5a7430 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/content.json @@ -0,0 +1,77 @@ +[ + { + "description": "validation of string-encoded content based on media type", + "schema": { + "contentMediaType": "application/json" + }, + "tests": [ + { + "description": "a valid JSON document", + "data": "{\"foo\": \"bar\"}", + "valid": true + }, + { + "description": "an invalid JSON document", + "data": "{:}", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary string-encoding", + "schema": { + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64 string", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "an invalid base64 string (% is not a valid character)", + "data": "eyJmb28iOi%iYmFyIn0K", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary-encoded media type documents", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document", + "data": "ezp9Cg==", + "valid": false + }, + { + "description": "an invalid base64 string that is valid JSON", + "data": "{}", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json new file mode 100755 index 00000000..6ed6cbe4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json @@ -0,0 +1,292 @@ +[ + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\t to horizontal tab", + "schema": { + "type": "string", + "pattern": "^\\t$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\t", + "valid": false + }, + { + "description": "matches", + "data": "\u0009", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\W matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches whitespace", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "Character tabulation matches", + "data": "\t", + "valid": true + }, + { + "description": "Line tabulation matches", + "data": "\u000b", + "valid": true + }, + { + "description": "Form feed matches", + "data": "\u000c", + "valid": true + }, + { + "description": "latin-1 non-breaking-space matches", + "data": "\u00a0", + "valid": true + }, + { + "description": "zero-width whitespace matches", + "data": "\ufeff", + "valid": true + }, + { + "description": "line feed matches (line terminator)", + "data": "\u000a", + "valid": true + }, + { + "description": "paragraph separator matches (line terminator)", + "data": "\u2029", + "valid": true + }, + { + "description": "EM SPACE matches (Space_Separator)", + "data": "\u2003", + "valid": true + }, + { + "description": "Non-whitespace control does not match", + "data": "\u0001", + "valid": false + }, + { + "description": "Non-whitespace does not match", + "data": "\u2013", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "Character tabulation does not match", + "data": "\t", + "valid": false + }, + { + "description": "Line tabulation does not match", + "data": "\u000b", + "valid": false + }, + { + "description": "Form feed does not match", + "data": "\u000c", + "valid": false + }, + { + "description": "latin-1 non-breaking-space does not match", + "data": "\u00a0", + "valid": false + }, + { + "description": "zero-width whitespace does not match", + "data": "\ufeff", + "valid": false + }, + { + "description": "line feed does not match (line terminator)", + "data": "\u000a", + "valid": false + }, + { + "description": "paragraph separator does not match (line terminator)", + "data": "\u2029", + "valid": false + }, + { + "description": "EM SPACE does not match (Space_Separator)", + "data": "\u2003", + "valid": false + }, + { + "description": "Non-whitespace control matches", + "data": "\u0001", + "valid": true + }, + { + "description": "Non-whitespace matches", + "data": "\u2013", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/float-overflow.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/float-overflow.json new file mode 100755 index 00000000..52ff9827 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/float-overflow.json @@ -0,0 +1,13 @@ +[ + { + "description": "all integers are multiples of 0.5, if overflow is handled", + "schema": {"type": "integer", "multipleOf": 0.5}, + "tests": [ + { + "description": "valid if optional overflow handling is implemented", + "data": 1e308, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json new file mode 100755 index 00000000..900fcb7c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date-time.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "a valid date-time string without second fraction", + "data": "1963-06-19T08:30:06Z", + "valid": true + }, + { + "description": "a valid date-time string with plus offset", + "data": "1937-01-01T12:00:27.87+00:20", + "valid": true + }, + { + "description": "a valid date-time string with minus offset", + "data": "1990-12-31T15:59:50.123-08:00", + "valid": true + }, + { + "description": "a invalid day in date-time string", + "data": "1990-02-31T15:59:60.123-08:00", + "valid": false + }, + { + "description": "an invalid offset in date-time string", + "data": "1990-12-31T15:59:60-24:00", + "valid": false + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date.json new file mode 100755 index 00000000..453b51de --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/date.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "a valid date string", + "data": "1963-06-19", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350", + "valid": false + }, + { + "description": "invalidates non-padded month dates", + "data": "1998-1-20", + "valid": false + }, + { + "description": "invalidates non-padded day dates", + "data": "1998-01-1", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json new file mode 100755 index 00000000..476541a8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/hostname.json @@ -0,0 +1,68 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a valid punycoded IDN hostname", + "data": "xn--4gbwdl.xn--wgbh1c", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json new file mode 100755 index 00000000..552d1067 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-email.json @@ -0,0 +1,28 @@ +[ + { + "description": "validation of an internationalized e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "a valid idn e-mail (example@example.test in Hangul)", + "data": "실례@실례.테스트", + "valid": true + }, + { + "description": "an invalid idn e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-hostname.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-hostname.json new file mode 100755 index 00000000..7f10bd83 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/idn-hostname.json @@ -0,0 +1,274 @@ +[ + { + "description": "validation of internationalized host names", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "a valid host name (example.test in Hangul)", + "data": "실례.테스트", + "valid": true + }, + { + "description": "illegal first char U+302E Hangul single dot tone mark", + "data": "〮실례.테스트", + "valid": false + }, + { + "description": "contains illegal char U+302E Hangul single dot tone mark", + "data": "실〮례.테스트", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트", + "valid": false + }, + { + "description": "invalid label, correct Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1", + "data": "-> $1.00 <--", + "valid": false + }, + { + "description": "valid Chinese Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4", + "data": "xn--ihqwcrb4cv8a8dqg056pqjye", + "valid": true + }, + { + "description": "invalid Punycode", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "xn--X", + "valid": false + }, + { + "description": "U-label contains \"--\" in the 3rd and 4th position", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "XN--aa---o47jg78q", + "valid": false + }, + { + "description": "U-label starts with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello", + "valid": false + }, + { + "description": "U-label ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "hello-", + "valid": false + }, + { + "description": "U-label starts and ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello-", + "valid": false + }, + { + "description": "Begins with a Spacing Combining Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0903hello", + "valid": false + }, + { + "description": "Begins with a Nonspacing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0300hello", + "valid": false + }, + { + "description": "Begins with an Enclosing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0488hello", + "valid": false + }, + { + "description": "Exceptions that are PVALID, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u00df\u03c2\u0f0b\u3007", + "valid": true + }, + { + "description": "Exceptions that are PVALID, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u06fd\u06fe", + "valid": true + }, + { + "description": "Exceptions that are DISALLOWED, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u0640\u07fa", + "valid": false + }, + { + "description": "Exceptions that are DISALLOWED, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start", + "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b", + "valid": false + }, + { + "description": "MIDDLE DOT with no preceding 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "a\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing preceding", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with no following 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7a", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing following", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7", + "valid": false + }, + { + "description": "MIDDLE DOT with surrounding 'l's", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7l", + "valid": true + }, + { + "description": "Greek KERAIA not followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375S", + "valid": false + }, + { + "description": "Greek KERAIA not followed by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375", + "valid": false + }, + { + "description": "Greek KERAIA followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375\u03b2", + "valid": true + }, + { + "description": "Hebrew GERESH not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "A\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05d0\u05f3\u05d1", + "valid": true + }, + { + "description": "Hebrew GERSHAYIM not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "A\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05d0\u05f4\u05d1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "def\u30fbabc", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with no other characters", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with Hiragana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u3041", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Katakana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u30a1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u4e08", + "valid": true + }, + { + "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", + "valid": false + }, + { + "description": "Arabic-Indic digits not 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": "\u0628\u0660\u0628", + "valid": true + }, + { + "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9", + "data": "\u06f00", + "valid": true + }, + { + "description": "ZERO WIDTH JOINER not preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u094d\u200d\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1", + "data": "\u0915\u094d\u200c\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement", + "data": "\u0628\u064a\u200c\u0628\u064a", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json new file mode 100755 index 00000000..8b99b9fb --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv4.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + }, + { + "description": "an IP address as an integer (decimal)", + "data": "2130706433", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv6.json new file mode 100755 index 00000000..2a08cb46 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/ipv6.json @@ -0,0 +1,153 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::42:ff:1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "missing leading octet is invalid", + "data": ":2:3:4:5:6:7:8", + "valid": false + }, + { + "description": "missing trailing octet is invalid", + "data": "1:2:3:4:5:6:7:", + "valid": false + }, + { + "description": "missing leading octet with omitted octets later", + "data": ":2:3:4::8", + "valid": false + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + }, + { + "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)", + "data": "::ffff:192.168.0.1", + "valid": true + }, + { + "description": "triple colons is invalid", + "data": "1:2:3:4:5:::8", + "valid": false + }, + { + "description": "8 octets", + "data": "1:2:3:4:5:6:7:8", + "valid": true + }, + { + "description": "insufficient octets without double colons", + "data": "1:2:3:4:5:6:7", + "valid": false + }, + { + "description": "no colons is invalid", + "data": "1", + "valid": false + }, + { + "description": "ipv4 is not ipv6", + "data": "127.0.0.1", + "valid": false + }, + { + "description": "ipv4 segment must have 4 octets", + "data": "1:2:3:4:1.2.3", + "valid": false + }, + { + "description": "leading whitespace is invalid", + "data": " ::1", + "valid": false + }, + { + "description": "trailing whitespace is invalid", + "data": "::1 ", + "valid": false + }, + { + "description": "netmask is not a part of ipv6 address", + "data": "fe80::/64", + "valid": false + }, + { + "description": "zone id is not a part of ipv6 address", + "data": "fe80::a%eth1", + "valid": false + }, + { + "description": "a long valid ipv6", + "data": "1000:1000:1000:1000:1000:1000:255.255.255.255", + "valid": true + }, + { + "description": "a long invalid ipv6, below length limit, first", + "data": "100:100:100:100:100:100:255.255.255.255.255", + "valid": false + }, + { + "description": "a long invalid ipv6, below length limit, second", + "data": "100:100:100:100:100:100:100:255.255.255.255", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json new file mode 100755 index 00000000..1fd779c2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of IRI References", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "a valid IRI", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid protocol-relative IRI Reference", + "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid relative IRI Reference", + "data": "/âππ", + "valid": true + }, + { + "description": "an invalid IRI Reference", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "a valid IRI Reference", + "data": "âππ", + "valid": true + }, + { + "description": "a valid IRI fragment", + "data": "#ƒrägmênt", + "valid": true + }, + { + "description": "an invalid IRI fragment", + "data": "#ƒräg\\mênt", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json new file mode 100755 index 00000000..ed54094c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/iri.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "a valid IRI with anchor tag", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid IRI with anchor tag and parantheses", + "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", + "valid": true + }, + { + "description": "a valid IRI with URL-encoded stuff", + "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid IRI with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid IRI based on IPv6", + "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", + "valid": true + }, + { + "description": "an invalid IRI based on IPv6", + "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "valid": false + }, + { + "description": "an invalid relative IRI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid IRI", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "an invalid IRI though valid IRI reference", + "data": "âππ", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/json-pointer.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/json-pointer.json new file mode 100755 index 00000000..65c2f064 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/json-pointer.json @@ -0,0 +1,168 @@ +[ + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json new file mode 100755 index 00000000..d99d021e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/regex.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "a valid regular expression", + "data": "([abc])+\\s+$", + "valid": true + }, + { + "description": "a regular expression with unclosed parens is invalid", + "data": "^(abc]", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json new file mode 100755 index 00000000..17816c9e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/relative-json-pointer.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of Relative JSON Pointers (RJP)", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "a valid upwards RJP", + "data": "1", + "valid": true + }, + { + "description": "a valid downwards RJP", + "data": "0/foo/bar", + "valid": true + }, + { + "description": "a valid up and then down RJP, with array index", + "data": "2/0/baz/1/zip", + "valid": true + }, + { + "description": "a valid RJP taking the member or index name", + "data": "0#", + "valid": true + }, + { + "description": "an invalid RJP that is a valid JSON Pointer", + "data": "/foo/bar", + "valid": false + }, + { + "description": "negative prefix", + "data": "-1/foo/bar", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/time.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/time.json new file mode 100755 index 00000000..4ec8a01a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/time.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "a valid time string", + "data": "08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid time string", + "data": "08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "01:01:01,1111", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json new file mode 100755 index 00000000..e4c9eef6 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json new file mode 100755 index 00000000..33ab76ee --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri-template.json @@ -0,0 +1,28 @@ +[ + { + "description": "format: uri-template", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json new file mode 100755 index 00000000..4306a686 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/format/uri.json @@ -0,0 +1,108 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + }, + { + "description": "an invalid URI with comma in scheme", + "data": "bar,baz:foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/draft7/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/pattern.json b/test/JSON-Schema-Test-Suite/tests/draft7/pattern.json new file mode 100755 index 00000000..92db0f97 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/pattern.json @@ -0,0 +1,59 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid", + "data": "abc", + "valid": false + }, + { + "description": "ignores booleans", + "data": true, + "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json new file mode 100755 index 00000000..c10ffcc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/patternProperties.json @@ -0,0 +1,156 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "object with a property matching both true and false is invalid", + "data": {"foobar":1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/properties.json b/test/JSON-Schema-Test-Suite/tests/draft7/properties.json new file mode 100755 index 00000000..b86c1819 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/properties.json @@ -0,0 +1,167 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + }, + { + "description": "properties with escaped characters", + "schema": { + "properties": { + "foo\nbar": {"type": "number"}, + "foo\"bar": {"type": "number"}, + "foo\\bar": {"type": "number"}, + "foo\rbar": {"type": "number"}, + "foo\tbar": {"type": "number"}, + "foo\fbar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with all numbers is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1", + "foo\\bar": "1", + "foo\rbar": "1", + "foo\tbar": "1", + "foo\fbar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json b/test/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json new file mode 100755 index 00000000..8423690d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/ref.json b/test/JSON-Schema-Test-Suite/tests/draft7/ref.json new file mode 100755 index 00000000..97ddf07c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/ref.json @@ -0,0 +1,493 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "definitions": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, + "properties": { + "tilde": {"$ref": "#/definitions/tilde~0field"}, + "slash": {"$ref": "#/definitions/slash~1field"}, + "percent": {"$ref": "#/definitions/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "definitions": { + "a": {"type": "integer"}, + "b": {"$ref": "#/definitions/a"}, + "c": {"$ref": "#/definitions/b"} + }, + "$ref": "#/definitions/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref overrides any sibling keywords", + "schema": { + "definitions": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/definitions/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems ignored", + "data": { "foo": [ 1, 2, 3] }, + "valid": true + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/definitions/is-string"} + }, + "definitions": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/definitions/bool", + "definitions": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "definitions": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + }, + { + "description": "refs with quote", + "schema": { + "properties": { + "foo\"bar": {"$ref": "#/definitions/foo%22bar"} + }, + "definitions": { + "foo\"bar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with numbers is valid", + "data": { + "foo\"bar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\"bar": "1" + }, + "valid": false + } + ] + }, + { + "description": "Location-independent identifier", + "schema": { + "allOf": [{ + "$ref": "#foo" + }], + "definitions": { + "A": { + "$id": "#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "allOf": [{ + "$ref": "http://localhost:1234/bar#foo" + }], + "definitions": { + "A": { + "$id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "$id": "http://localhost:1234/root", + "allOf": [{ + "$ref": "http://localhost:1234/nested.json#foo" + }], + "definitions": { + "A": { + "$id": "nested.json", + "definitions": { + "B": { + "$id": "#foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "definitions": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/definitions/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/definitions/a_string" }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/refRemote.json b/test/JSON-Schema-Test-Suite/tests/draft7/refRemote.json new file mode 100755 index 00000000..74a78626 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/refRemote.json @@ -0,0 +1,171 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "baseUriChange/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz"} + }, + "definitions": { + "baz": { + "$id": "baseUriChangeFolder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": { + "list": {"$ref": "#/definitions/baz/definitions/bar"} + }, + "definitions": { + "baz": { + "$id": "baseUriChangeFolderInSubschema/", + "definitions": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name.json#/definitions/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/required.json b/test/JSON-Schema-Test-Suite/tests/draft7/required.json new file mode 100755 index 00000000..abf18f34 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/required.json @@ -0,0 +1,105 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with escaped characters", + "schema": { + "required": [ + "foo\nbar", + "foo\"bar", + "foo\\bar", + "foo\rbar", + "foo\tbar", + "foo\fbar" + ] + }, + "tests": [ + { + "description": "object with all properties present is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with some properties missing is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/type.json b/test/JSON-Schema-Test-Suite/tests/draft7/type.json new file mode 100755 index 00000000..83046470 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/type.json @@ -0,0 +1,474 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is an integer", + "data": 1.0, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is a number (and an integer)", + "data": 1.0, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an empty string is still a string", + "data": "", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "zero is not a boolean", + "data": 0, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not a boolean", + "data": "", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "true is a boolean", + "data": true, + "valid": true + }, + { + "description": "false is a boolean", + "data": false, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "zero is not null", + "data": 0, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not null", + "data": "", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "true is not null", + "data": true, + "valid": false + }, + { + "description": "false is not null", + "data": false, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type as array with one item", + "schema": { + "type": ["string"] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "type: array or object", + "schema": { + "type": ["array", "object"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type: array, object or null", + "schema": { + "type": ["array", "object", "null"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/draft7/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/draft7/uniqueItems.json new file mode 100755 index 00000000..4846c773 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/draft7/uniqueItems.json @@ -0,0 +1,384 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1, "{}"], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + }, + { + "description": "different objects are unique", + "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], + "valid": true + }, + { + "description": "objects are non-unique despite key order", + "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], + "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/additionalItems.json b/test/JSON-Schema-Test-Suite/tests/latest/additionalItems.json new file mode 100755 index 00000000..ee46b61a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/additionalItems.json @@ -0,0 +1,130 @@ +[ + { + "description": "additionalItems as schema", + "schema": { + "items": [{}], + "additionalItems": {"type": "integer"} + }, + "tests": [ + { + "description": "additional items match schema", + "data": [ null, 2, 3, 4 ], + "valid": true + }, + { + "description": "additional items do not match schema", + "data": [ null, 2, 3, "foo" ], + "valid": false + } + ] + }, + { + "description": "items is schema, no additionalItems", + "schema": { + "items": {}, + "additionalItems": false + }, + "tests": [ + { + "description": "all items match schema", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + } + ] + }, + { + "description": "array of items with no additionalItems", + "schema": { + "items": [{}, {}, {}], + "additionalItems": false + }, + "tests": [ + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "fewer number of items present (1)", + "data": [ 1 ], + "valid": true + }, + { + "description": "fewer number of items present (2)", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "equal number of items present", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "additional items are not permitted", + "data": [ 1, 2, 3, 4 ], + "valid": false + } + ] + }, + { + "description": "additionalItems as false without items", + "schema": {"additionalItems": false}, + "tests": [ + { + "description": + "items defaults to empty schema so everything is valid", + "data": [ 1, 2, 3, 4, 5 ], + "valid": true + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + } + ] + }, + { + "description": "additionalItems are allowed by default", + "schema": {"items": [{"type": "integer"}]}, + "tests": [ + { + "description": "only the first item is validated", + "data": [1, "foo", false], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, valid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" } ] } + ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, null ], + "valid": true + } + ] + }, + { + "description": "additionalItems should not look in applicators, invalid case", + "schema": { + "allOf": [ + { "items": [ { "type": "integer" }, { "type": "string" } ] } + ], + "items": [ {"type": "integer" } ], + "additionalItems": { "type": "boolean" } + }, + "tests": [ + { + "description": "items defined in allOf are not examined", + "data": [ 1, "hello" ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/additionalProperties.json b/test/JSON-Schema-Test-Suite/tests/latest/additionalProperties.json new file mode 100755 index 00000000..381275a5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/additionalProperties.json @@ -0,0 +1,133 @@ +[ + { + "description": + "additionalProperties being false does not allow other properties", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "patternProperties": { "^v": {} }, + "additionalProperties": false + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobarbaz", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + }, + { + "description": "patternProperties are not additional properties", + "data": {"foo":1, "vroom": 2}, + "valid": true + } + ] + }, + { + "description": "non-ASCII pattern with additionalProperties", + "schema": { + "patternProperties": {"^á": {}}, + "additionalProperties": false + }, + "tests": [ + { + "description": "matching the pattern is valid", + "data": {"ármányos": 2}, + "valid": true + }, + { + "description": "not matching the pattern is invalid", + "data": {"élmény": 2}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties allows a schema which should validate", + "schema": { + "properties": {"foo": {}, "bar": {}}, + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "no additional properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "an additional valid property is valid", + "data": {"foo" : 1, "bar" : 2, "quux" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1, "bar" : 2, "quux" : 12}, + "valid": false + } + ] + }, + { + "description": + "additionalProperties can exist by itself", + "schema": { + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "an additional valid property is valid", + "data": {"foo" : true}, + "valid": true + }, + { + "description": "an additional invalid property is invalid", + "data": {"foo" : 1}, + "valid": false + } + ] + }, + { + "description": "additionalProperties are allowed by default", + "schema": {"properties": {"foo": {}, "bar": {}}}, + "tests": [ + { + "description": "additional properties are allowed", + "data": {"foo": 1, "bar": 2, "quux": true}, + "valid": true + } + ] + }, + { + "description": "additionalProperties should not look in applicators", + "schema": { + "allOf": [ + {"properties": {"foo": {}}} + ], + "additionalProperties": {"type": "boolean"} + }, + "tests": [ + { + "description": "properties defined in allOf are not examined", + "data": {"foo": 1, "bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/allOf.json b/test/JSON-Schema-Test-Suite/tests/latest/allOf.json new file mode 100755 index 00000000..ec9319e1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/allOf.json @@ -0,0 +1,294 @@ +[ + { + "description": "allOf", + "schema": { + "allOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "allOf", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "mismatch second", + "data": {"foo": "baz"}, + "valid": false + }, + { + "description": "mismatch first", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "wrong type", + "data": {"foo": "baz", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "allOf with base schema", + "schema": { + "properties": {"bar": {"type": "integer"}}, + "required": ["bar"], + "allOf" : [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }, + "tests": [ + { + "description": "valid", + "data": {"foo": "quux", "bar": 2, "baz": null}, + "valid": true + }, + { + "description": "mismatch base schema", + "data": {"foo": "quux", "baz": null}, + "valid": false + }, + { + "description": "mismatch first allOf", + "data": {"bar": 2, "baz": null}, + "valid": false + }, + { + "description": "mismatch second allOf", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "mismatch both", + "data": {"bar": 2}, + "valid": false + } + ] + }, + { + "description": "allOf simple types", + "schema": { + "allOf": [ + {"maximum": 30}, + {"minimum": 20} + ] + }, + "tests": [ + { + "description": "valid", + "data": 25, + "valid": true + }, + { + "description": "mismatch one", + "data": 35, + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all true", + "schema": {"allOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "allOf with boolean schemas, some false", + "schema": {"allOf": [true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with boolean schemas, all false", + "schema": {"allOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with one empty schema", + "schema": { + "allOf": [ + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with two empty schemas", + "schema": { + "allOf": [ + {}, + {} + ] + }, + "tests": [ + { + "description": "any data is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "allOf with the first empty schema", + "schema": { + "allOf": [ + {}, + { "type": "number" } + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "allOf with the last empty schema", + "schema": { + "allOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "nested allOf, to check validation semantics", + "schema": { + "allOf": [ + { + "allOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "allOf combined with anyOf, oneOf", + "schema": { + "allOf": [ { "multipleOf": 2 } ], + "anyOf": [ { "multipleOf": 3 } ], + "oneOf": [ { "multipleOf": 5 } ] + }, + "tests": [ + { + "description": "allOf: false, anyOf: false, oneOf: false", + "data": 1, + "valid": false + }, + { + "description": "allOf: false, anyOf: false, oneOf: true", + "data": 5, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: false", + "data": 3, + "valid": false + }, + { + "description": "allOf: false, anyOf: true, oneOf: true", + "data": 15, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: false", + "data": 2, + "valid": false + }, + { + "description": "allOf: true, anyOf: false, oneOf: true", + "data": 10, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: false", + "data": 6, + "valid": false + }, + { + "description": "allOf: true, anyOf: true, oneOf: true", + "data": 30, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/anchor.json b/test/JSON-Schema-Test-Suite/tests/latest/anchor.json new file mode 100755 index 00000000..42dde7e4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/anchor.json @@ -0,0 +1,81 @@ +[ + { + "description": "Location-independent identifier", + "schema": { + "$ref": "#foo", + "$defs": { + "A": { + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with absolute URI", + "schema": { + "$ref": "http://localhost:1234/bar#foo", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar", + "$anchor": "foo", + "type": "integer" + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + }, + { + "description": "Location-independent identifier with base URI change in subschema", + "schema": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/nested.json#foo", + "$defs": { + "A": { + "$id": "nested.json", + "$defs": { + "B": { + "$anchor": "foo", + "type": "integer" + } + } + } + } + }, + "tests": [ + { + "data": 1, + "description": "match", + "valid": true + }, + { + "data": "a", + "description": "mismatch", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/anyOf.json b/test/JSON-Schema-Test-Suite/tests/latest/anyOf.json new file mode 100755 index 00000000..ab5eb386 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/anyOf.json @@ -0,0 +1,189 @@ +[ + { + "description": "anyOf", + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first anyOf valid", + "data": 1, + "valid": true + }, + { + "description": "second anyOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both anyOf valid", + "data": 3, + "valid": true + }, + { + "description": "neither anyOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "anyOf with base schema", + "schema": { + "type": "string", + "anyOf" : [ + { + "maxLength": 2 + }, + { + "minLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one anyOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both anyOf invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf with boolean schemas, all true", + "schema": {"anyOf": [true, true]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, some true", + "schema": {"anyOf": [true, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "anyOf with boolean schemas, all false", + "schema": {"anyOf": [false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "anyOf complex types", + "schema": { + "anyOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first anyOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second anyOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both anyOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": true + }, + { + "description": "neither anyOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "anyOf with one empty schema", + "schema": { + "anyOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is valid", + "data": 123, + "valid": true + } + ] + }, + { + "description": "nested anyOf, to check validation semantics", + "schema": { + "anyOf": [ + { + "anyOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/boolean_schema.json b/test/JSON-Schema-Test-Suite/tests/latest/boolean_schema.json new file mode 100755 index 00000000..6d40f23f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/boolean_schema.json @@ -0,0 +1,104 @@ +[ + { + "description": "boolean schema 'true'", + "schema": true, + "tests": [ + { + "description": "number is valid", + "data": 1, + "valid": true + }, + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "boolean true is valid", + "data": true, + "valid": true + }, + { + "description": "boolean false is valid", + "data": false, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": "bar"}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "boolean schema 'false'", + "schema": false, + "tests": [ + { + "description": "number is invalid", + "data": 1, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "boolean true is invalid", + "data": true, + "valid": false + }, + { + "description": "boolean false is invalid", + "data": false, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + }, + { + "description": "object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/const.json b/test/JSON-Schema-Test-Suite/tests/latest/const.json new file mode 100755 index 00000000..1c2cafcc --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/const.json @@ -0,0 +1,342 @@ +[ + { + "description": "const validation", + "schema": {"const": 2}, + "tests": [ + { + "description": "same value is valid", + "data": 2, + "valid": true + }, + { + "description": "another value is invalid", + "data": 5, + "valid": false + }, + { + "description": "another type is invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "const with object", + "schema": {"const": {"foo": "bar", "baz": "bax"}}, + "tests": [ + { + "description": "same object is valid", + "data": {"foo": "bar", "baz": "bax"}, + "valid": true + }, + { + "description": "same object with different property order is valid", + "data": {"baz": "bax", "foo": "bar"}, + "valid": true + }, + { + "description": "another object is invalid", + "data": {"foo": "bar"}, + "valid": false + }, + { + "description": "another type is invalid", + "data": [1, 2], + "valid": false + } + ] + }, + { + "description": "const with array", + "schema": {"const": [{ "foo": "bar" }]}, + "tests": [ + { + "description": "same array is valid", + "data": [{"foo": "bar"}], + "valid": true + }, + { + "description": "another array item is invalid", + "data": [2], + "valid": false + }, + { + "description": "array with additional items is invalid", + "data": [1, 2, 3], + "valid": false + } + ] + }, + { + "description": "const with null", + "schema": {"const": null}, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "not null is invalid", + "data": 0, + "valid": false + } + ] + }, + { + "description": "const with false does not match 0", + "schema": {"const": false}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "const with true does not match 1", + "schema": {"const": true}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "const with [false] does not match [0]", + "schema": {"const": [false]}, + "tests": [ + { + "description": "[false] is valid", + "data": [false], + "valid": true + }, + { + "description": "[0] is invalid", + "data": [0], + "valid": false + }, + { + "description": "[0.0] is invalid", + "data": [0.0], + "valid": false + } + ] + }, + { + "description": "const with [true] does not match [1]", + "schema": {"const": [true]}, + "tests": [ + { + "description": "[true] is valid", + "data": [true], + "valid": true + }, + { + "description": "[1] is invalid", + "data": [1], + "valid": false + }, + { + "description": "[1.0] is invalid", + "data": [1.0], + "valid": false + } + ] + }, + { + "description": "const with {\"a\": false} does not match {\"a\": 0}", + "schema": {"const": {"a": false}}, + "tests": [ + { + "description": "{\"a\": false} is valid", + "data": {"a": false}, + "valid": true + }, + { + "description": "{\"a\": 0} is invalid", + "data": {"a": 0}, + "valid": false + }, + { + "description": "{\"a\": 0.0} is invalid", + "data": {"a": 0.0}, + "valid": false + } + ] + }, + { + "description": "const with {\"a\": true} does not match {\"a\": 1}", + "schema": {"const": {"a": true}}, + "tests": [ + { + "description": "{\"a\": true} is valid", + "data": {"a": true}, + "valid": true + }, + { + "description": "{\"a\": 1} is invalid", + "data": {"a": 1}, + "valid": false + }, + { + "description": "{\"a\": 1.0} is invalid", + "data": {"a": 1.0}, + "valid": false + } + ] + }, + { + "description": "const with 0 does not match other zero-like types", + "schema": {"const": 0}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + }, + { + "description": "empty object is invalid", + "data": {}, + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "empty string is invalid", + "data": "", + "valid": false + } + ] + }, + { + "description": "const with 1 does not match true", + "schema": {"const": 1}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "const with -2.0 matches integer and float types", + "schema": {"const": -2.0}, + "tests": [ + { + "description": "integer -2 is valid", + "data": -2, + "valid": true + }, + { + "description": "integer 2 is invalid", + "data": 2, + "valid": false + }, + { + "description": "float -2.0 is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float 2.0 is invalid", + "data": 2.0, + "valid": false + }, + { + "description": "float -2.00001 is invalid", + "data": -2.00001, + "valid": false + } + ] + }, + { + "description": "float and integers are equal up to 64-bit representation limits", + "schema": {"const": 9007199254740992}, + "tests": [ + { + "description": "integer is valid", + "data": 9007199254740992, + "valid": true + }, + { + "description": "integer minus one is invalid", + "data": 9007199254740991, + "valid": false + }, + { + "description": "float is valid", + "data": 9007199254740992.0, + "valid": true + }, + { + "description": "float minus one is invalid", + "data": 9007199254740991.0, + "valid": false + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "const": "hello\u0000there" }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/contains.json b/test/JSON-Schema-Test-Suite/tests/latest/contains.json new file mode 100755 index 00000000..c5471cc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/contains.json @@ -0,0 +1,129 @@ +[ + { + "description": "contains keyword validation", + "schema": { + "contains": {"minimum": 5} + }, + "tests": [ + { + "description": "array with item matching schema (5) is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with item matching schema (6) is valid", + "data": [3, 4, 6], + "valid": true + }, + { + "description": "array with two items matching schema (5, 6) is valid", + "data": [3, 4, 5, 6], + "valid": true + }, + { + "description": "array without items matching schema is invalid", + "data": [2, 3, 4], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "not array is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "contains keyword with const keyword", + "schema": { + "contains": { "const": 5 } + }, + "tests": [ + { + "description": "array with item 5 is valid", + "data": [3, 4, 5], + "valid": true + }, + { + "description": "array with two items 5 is valid", + "data": [3, 4, 5, 5], + "valid": true + }, + { + "description": "array without item 5 is invalid", + "data": [1, 2, 3, 4], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema true", + "schema": {"contains": true}, + "tests": [ + { + "description": "any non-empty array is valid", + "data": ["foo"], + "valid": true + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + } + ] + }, + { + "description": "contains keyword with boolean schema false", + "schema": {"contains": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": ["foo"], + "valid": false + }, + { + "description": "empty array is invalid", + "data": [], + "valid": false + }, + { + "description": "non-arrays are valid", + "data": "contains does not apply to strings", + "valid": true + } + ] + }, + { + "description": "items + contains", + "schema": { + "items": { "multipleOf": 2 }, + "contains": { "multipleOf": 3 } + }, + "tests": [ + { + "description": "matches items, does not match contains", + "data": [ 2, 4, 8 ], + "valid": false + }, + { + "description": "does not match items, matches contains", + "data": [ 3, 6, 9 ], + "valid": false + }, + { + "description": "matches both items and contains", + "data": [ 6, 12 ], + "valid": true + }, + { + "description": "matches neither items nor contains", + "data": [ 1, 5 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/content.json b/test/JSON-Schema-Test-Suite/tests/latest/content.json new file mode 100755 index 00000000..44688e82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/content.json @@ -0,0 +1,127 @@ +[ + { + "description": "validation of string-encoded content based on media type", + "schema": { + "contentMediaType": "application/json" + }, + "tests": [ + { + "description": "a valid JSON document", + "data": "{\"foo\": \"bar\"}", + "valid": true + }, + { + "description": "an invalid JSON document; validates true", + "data": "{:}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary string-encoding", + "schema": { + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64 string", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "an invalid base64 string (% is not a valid character); validates true", + "data": "eyJmb28iOi%iYmFyIn0K", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary-encoded media type documents", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64" + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document; validates true", + "data": "ezp9Cg==", + "valid": true + }, + { + "description": "an invalid base64 string that is valid JSON; validates true", + "data": "{}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + }, + { + "description": "validation of binary-encoded media type documents with schema", + "schema": { + "contentMediaType": "application/json", + "contentEncoding": "base64", + "contentSchema": { "required": ["foo"], "properties": { "foo": { "type": "string" } } } + }, + "tests": [ + { + "description": "a valid base64-encoded JSON document", + "data": "eyJmb28iOiAiYmFyIn0K", + "valid": true + }, + { + "description": "another valid base64-encoded JSON document", + "data": "eyJib28iOiAyMCwgImZvbyI6ICJiYXoifQ==", + "valid": true + }, + { + "description": "an invalid base64-encoded JSON document; validates true", + "data": "eyJib28iOiAyMH0=", + "valid": true + }, + { + "description": "an empty object as a base64-encoded JSON document; validates true", + "data": "e30=", + "valid": true + }, + { + "description": "an empty array as a base64-encoded JSON document", + "data": "W10=", + "valid": true + }, + { + "description": "a validly-encoded invalid JSON document; validates true", + "data": "ezp9Cg==", + "valid": true + }, + { + "description": "an invalid base64 string that is valid JSON; validates true", + "data": "{}", + "valid": true + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/default.json b/test/JSON-Schema-Test-Suite/tests/latest/default.json new file mode 100755 index 00000000..17629779 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/default.json @@ -0,0 +1,49 @@ +[ + { + "description": "invalid type for default", + "schema": { + "properties": { + "foo": { + "type": "integer", + "default": [] + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"foo": 13}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + }, + { + "description": "invalid string value for default", + "schema": { + "properties": { + "bar": { + "type": "string", + "minLength": 4, + "default": "bad" + } + } + }, + "tests": [ + { + "description": "valid when property is specified", + "data": {"bar": "good"}, + "valid": true + }, + { + "description": "still valid when the invalid default is used", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/defs.json b/test/JSON-Schema-Test-Suite/tests/latest/defs.json new file mode 100755 index 00000000..f2fbec42 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/defs.json @@ -0,0 +1,24 @@ +[ + { + "description": "valid definition", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "valid definition schema", + "data": {"$defs": {"foo": {"type": "integer"}}}, + "valid": true + } + ] + }, + { + "description": "invalid definition", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "invalid definition schema", + "data": {"$defs": {"foo": {"type": 1}}}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/dependentRequired.json b/test/JSON-Schema-Test-Suite/tests/latest/dependentRequired.json new file mode 100755 index 00000000..c817120d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/dependentRequired.json @@ -0,0 +1,142 @@ +[ + { + "description": "single dependency", + "schema": {"dependentRequired": {"bar": ["foo"]}}, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependant", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "with dependency", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["bar"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "empty dependents", + "schema": {"dependentRequired": {"bar": []}}, + "tests": [ + { + "description": "empty object", + "data": {}, + "valid": true + }, + { + "description": "object with one property", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "non-object is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "multiple dependents required", + "schema": {"dependentRequired": {"quux": ["foo", "bar"]}}, + "tests": [ + { + "description": "neither", + "data": {}, + "valid": true + }, + { + "description": "nondependants", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "with dependencies", + "data": {"foo": 1, "bar": 2, "quux": 3}, + "valid": true + }, + { + "description": "missing dependency", + "data": {"foo": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing other dependency", + "data": {"bar": 1, "quux": 2}, + "valid": false + }, + { + "description": "missing both dependencies", + "data": {"quux": 1}, + "valid": false + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependentRequired": { + "foo\nbar": ["foo\rbar"], + "foo\"bar": ["foo'bar"] + } + }, + "tests": [ + { + "description": "CRLF", + "data": { + "foo\nbar": 1, + "foo\rbar": 2 + }, + "valid": true + }, + { + "description": "quoted quotes", + "data": { + "foo'bar": 1, + "foo\"bar": 2 + }, + "valid": true + }, + { + "description": "CRLF missing dependent", + "data": { + "foo\nbar": 1, + "foo": 2 + }, + "valid": false + }, + { + "description": "quoted quotes missing dependent", + "data": { + "foo\"bar": 2 + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/dependentSchemas.json b/test/JSON-Schema-Test-Suite/tests/latest/dependentSchemas.json new file mode 100755 index 00000000..e7921d1e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/dependentSchemas.json @@ -0,0 +1,114 @@ +[ + { + "description": "single dependency", + "schema": { + "dependentSchemas": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }, + "tests": [ + { + "description": "valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "no dependency", + "data": {"foo": "quux"}, + "valid": true + }, + { + "description": "wrong type", + "data": {"foo": "quux", "bar": 2}, + "valid": false + }, + { + "description": "wrong type other", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + }, + { + "description": "wrong type both", + "data": {"foo": "quux", "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "boolean subschemas", + "schema": { + "dependentSchemas": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "object with property having schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property having schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "dependencies with escaped characters", + "schema": { + "dependentSchemas": { + "foo\tbar": {"minProperties": 4}, + "foo'bar": {"required": ["foo\"bar"]} + } + }, + "tests": [ + { + "description": "quoted tab", + "data": { + "foo\tbar": 1, + "a": 2, + "b": 3, + "c": 4 + }, + "valid": true + }, + { + "description": "quoted quote", + "data": { + "foo'bar": {"foo\"bar": 1} + }, + "valid": false + }, + { + "description": "quoted tab invalid under dependent schema", + "data": { + "foo\tbar": 1, + "a": 2 + }, + "valid": false + }, + { + "description": "quoted quote invalid under dependent schema", + "data": {"foo'bar": 1}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/enum.json b/test/JSON-Schema-Test-Suite/tests/latest/enum.json new file mode 100755 index 00000000..f085097b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/enum.json @@ -0,0 +1,236 @@ +[ + { + "description": "simple enum validation", + "schema": {"enum": [1, 2, 3]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": 1, + "valid": true + }, + { + "description": "something else is invalid", + "data": 4, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum validation", + "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, + "tests": [ + { + "description": "one of the enum is valid", + "data": [], + "valid": true + }, + { + "description": "something else is invalid", + "data": null, + "valid": false + }, + { + "description": "objects are deep compared", + "data": {"foo": false}, + "valid": false + }, + { + "description": "valid object matches", + "data": {"foo": 12}, + "valid": true + }, + { + "description": "extra properties in object is invalid", + "data": {"foo": 12, "boo": 42}, + "valid": false + } + ] + }, + { + "description": "heterogeneous enum-with-null validation", + "schema": { "enum": [6, null] }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is valid", + "data": 6, + "valid": true + }, + { + "description": "something else is invalid", + "data": "test", + "valid": false + } + ] + }, + { + "description": "enums in properties", + "schema": { + "type":"object", + "properties": { + "foo": {"enum":["foo"]}, + "bar": {"enum":["bar"]} + }, + "required": ["bar"] + }, + "tests": [ + { + "description": "both properties are valid", + "data": {"foo":"foo", "bar":"bar"}, + "valid": true + }, + { + "description": "wrong foo value", + "data": {"foo":"foot", "bar":"bar"}, + "valid": false + }, + { + "description": "wrong bar value", + "data": {"foo":"foo", "bar":"bart"}, + "valid": false + }, + { + "description": "missing optional property is valid", + "data": {"bar":"bar"}, + "valid": true + }, + { + "description": "missing required property is invalid", + "data": {"foo":"foo"}, + "valid": false + }, + { + "description": "missing all properties is invalid", + "data": {}, + "valid": false + } + ] + }, + { + "description": "enum with escaped characters", + "schema": { + "enum": ["foo\nbar", "foo\rbar"] + }, + "tests": [ + { + "description": "member 1 is valid", + "data": "foo\nbar", + "valid": true + }, + { + "description": "member 2 is valid", + "data": "foo\rbar", + "valid": true + }, + { + "description": "another string is invalid", + "data": "abc", + "valid": false + } + ] + }, + { + "description": "enum with false does not match 0", + "schema": {"enum": [false]}, + "tests": [ + { + "description": "false is valid", + "data": false, + "valid": true + }, + { + "description": "integer zero is invalid", + "data": 0, + "valid": false + }, + { + "description": "float zero is invalid", + "data": 0.0, + "valid": false + } + ] + }, + { + "description": "enum with true does not match 1", + "schema": {"enum": [true]}, + "tests": [ + { + "description": "true is valid", + "data": true, + "valid": true + }, + { + "description": "integer one is invalid", + "data": 1, + "valid": false + }, + { + "description": "float one is invalid", + "data": 1.0, + "valid": false + } + ] + }, + { + "description": "enum with 0 does not match false", + "schema": {"enum": [0]}, + "tests": [ + { + "description": "false is invalid", + "data": false, + "valid": false + }, + { + "description": "integer zero is valid", + "data": 0, + "valid": true + }, + { + "description": "float zero is valid", + "data": 0.0, + "valid": true + } + ] + }, + { + "description": "enum with 1 does not match true", + "schema": {"enum": [1]}, + "tests": [ + { + "description": "true is invalid", + "data": true, + "valid": false + }, + { + "description": "integer one is valid", + "data": 1, + "valid": true + }, + { + "description": "float one is valid", + "data": 1.0, + "valid": true + } + ] + }, + { + "description": "nul characters in strings", + "schema": { "enum": [ "hello\u0000there" ] }, + "tests": [ + { + "description": "match string with nul", + "data": "hello\u0000there", + "valid": true + }, + { + "description": "do not match string lacking nul", + "data": "hellothere", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMaximum.json b/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMaximum.json new file mode 100755 index 00000000..dc3cd709 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMaximum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMaximum validation", + "schema": { + "exclusiveMaximum": 3.0 + }, + "tests": [ + { + "description": "below the exclusiveMaximum is valid", + "data": 2.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 3.0, + "valid": false + }, + { + "description": "above the exclusiveMaximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMinimum.json b/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMinimum.json new file mode 100755 index 00000000..b38d7ece --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/exclusiveMinimum.json @@ -0,0 +1,30 @@ +[ + { + "description": "exclusiveMinimum validation", + "schema": { + "exclusiveMinimum": 1.1 + }, + "tests": [ + { + "description": "above the exclusiveMinimum is valid", + "data": 1.2, + "valid": true + }, + { + "description": "boundary point is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "below the exclusiveMinimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/format.json b/test/JSON-Schema-Test-Suite/tests/latest/format.json new file mode 100755 index 00000000..dddea866 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/format.json @@ -0,0 +1,686 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of regexes", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IDN hostnames", + "schema": {"format": "idn-hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of hostnames", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of JSON pointers", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of relative JSON pointers", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of IRI references", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI references", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of URI templates", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of UUIDs", + "schema": { "format": "uuid" }, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "validation of durations", + "schema": { "format": "duration" }, + "tests": [ + { + "description": "ignores integers", + "data": 12, + "valid": true + }, + { + "description": "ignores floats", + "data": 13.7, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores booleans", + "data": false, + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/id.json b/test/JSON-Schema-Test-Suite/tests/latest/id.json new file mode 100755 index 00000000..cd97d596 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/id.json @@ -0,0 +1,206 @@ +[ + { + "description": "Invalid use of fragments in location-independent $id", + "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/bar#foo", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#foo", + "type": "integer" + } + } + }, + "valid": false + }, + { + "description": "Identifier path with absolute URI", + "data": { + "$ref": "http://localhost:1234/bar#/a/b", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#/a/b", + "type": "integer" + } + } + }, + "valid": false + }, + { + "description": "Identifier name with base URI change in subschema", + "data": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/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/root", + "$ref": "http://localhost:1234/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": { + "$ref": "https://json-schema.org/draft/2019-09/schema" + }, + "tests": [ + { + "description": "Identifier name with absolute URI", + "data": { + "$ref": "http://localhost:1234/bar", + "$defs": { + "A": { + "$id": "http://localhost:1234/bar#", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Identifier name with base URI change in subschema", + "data": { + "$id": "http://localhost:1234/root", + "$ref": "http://localhost:1234/nested.json#/$defs/B", + "$defs": { + "A": { + "$id": "nested.json", + "$defs": { + "B": { + "$id": "#", + "type": "integer" + } + } + } + } + }, + "valid": true + } + ] + }, + { + "description": "Unnormalized $ids are allowed but discouraged", + "schema": { + "$ref": "https://json-schema.org/draft/2019-09/schema" + }, + "tests": [ + { + "description": "Unnormalized identifier", + "data": { + "$ref": "http://localhost:1234/foo/baz", + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier and no ref", + "data": { + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier with empty fragment", + "data": { + "$ref": "http://localhost:1234/foo/baz", + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz#", + "type": "integer" + } + } + }, + "valid": true + }, + { + "description": "Unnormalized identifier with empty fragment and no ref", + "data": { + "$defs": { + "A": { + "$id": "http://localhost:1234/foo/bar/../baz#", + "type": "integer" + } + } + }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/if-then-else.json b/test/JSON-Schema-Test-Suite/tests/latest/if-then-else.json new file mode 100755 index 00000000..284e9191 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/if-then-else.json @@ -0,0 +1,258 @@ +[ + { + "description": "ignore if without then or else", + "schema": { + "if": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone if", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone if", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore then without if", + "schema": { + "then": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone then", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone then", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "ignore else without if", + "schema": { + "else": { + "const": 0 + } + }, + "tests": [ + { + "description": "valid when valid against lone else", + "data": 0, + "valid": true + }, + { + "description": "valid when invalid against lone else", + "data": "hello", + "valid": true + } + ] + }, + { + "description": "if and then without else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid when if test fails", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if and else without then", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid when if test passes", + "data": -1, + "valid": true + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "validate against correct branch, then vs else", + "schema": { + "if": { + "exclusiveMaximum": 0 + }, + "then": { + "minimum": -10 + }, + "else": { + "multipleOf": 2 + } + }, + "tests": [ + { + "description": "valid through then", + "data": -1, + "valid": true + }, + { + "description": "invalid through then", + "data": -100, + "valid": false + }, + { + "description": "valid through else", + "data": 4, + "valid": true + }, + { + "description": "invalid through else", + "data": 3, + "valid": false + } + ] + }, + { + "description": "non-interference across combined schemas", + "schema": { + "allOf": [ + { + "if": { + "exclusiveMaximum": 0 + } + }, + { + "then": { + "minimum": -10 + } + }, + { + "else": { + "multipleOf": 2 + } + } + ] + }, + "tests": [ + { + "description": "valid, but would have been invalid through then", + "data": -100, + "valid": true + }, + { + "description": "valid, but would have been invalid through else", + "data": 3, + "valid": true + } + ] + }, + { + "description": "if with boolean schema true", + "schema": { + "if": true, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema true in if always chooses the then path (valid)", + "data": "then", + "valid": true + }, + { + "description": "boolean schema true in if always chooses the then path (invalid)", + "data": "else", + "valid": false + } + ] + }, + { + "description": "if with boolean schema false", + "schema": { + "if": false, + "then": { "const": "then" }, + "else": { "const": "else" } + }, + "tests": [ + { + "description": "boolean schema false in if always chooses the else path (invalid)", + "data": "then", + "valid": false + }, + { + "description": "boolean schema false in if always chooses the else path (valid)", + "data": "else", + "valid": true + } + ] + }, + { + "description": "if appears at the end when serialized (keyword processing sequence)", + "schema": { + "then": { "const": "yes" }, + "else": { "const": "other" }, + "if": { "maxLength": 4 } + }, + "tests": [ + { + "description": "yes redirects to then and passes", + "data": "yes", + "valid": true + }, + { + "description": "other redirects to else and passes", + "data": "other", + "valid": true + }, + { + "description": "no redirects to then and fails", + "data": "no", + "valid": false + }, + { + "description": "invalid redirects to else and fails", + "data": "invalid", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/infinite-loop-detection.json b/test/JSON-Schema-Test-Suite/tests/latest/infinite-loop-detection.json new file mode 100755 index 00000000..9c3c3627 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/infinite-loop-detection.json @@ -0,0 +1,36 @@ +[ + { + "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", + "schema": { + "$defs": { + "int": { "type": "integer" } + }, + "allOf": [ + { + "properties": { + "foo": { + "$ref": "#/$defs/int" + } + } + }, + { + "additionalProperties": { + "$ref": "#/$defs/int" + } + } + ] + }, + "tests": [ + { + "description": "passing case", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "failing case", + "data": { "foo": "a string" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/items.json b/test/JSON-Schema-Test-Suite/tests/latest/items.json new file mode 100755 index 00000000..6e98ee82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/items.json @@ -0,0 +1,250 @@ +[ + { + "description": "a schema given for items", + "schema": { + "items": {"type": "integer"} + }, + "tests": [ + { + "description": "valid items", + "data": [ 1, 2, 3 ], + "valid": true + }, + { + "description": "wrong type of items", + "data": [1, "x"], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": {"foo" : "bar"}, + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "length": 1 + }, + "valid": true + } + ] + }, + { + "description": "an array of schemas for items", + "schema": { + "items": [ + {"type": "integer"}, + {"type": "string"} + ] + }, + "tests": [ + { + "description": "correct types", + "data": [ 1, "foo" ], + "valid": true + }, + { + "description": "wrong types", + "data": [ "foo", 1 ], + "valid": false + }, + { + "description": "incomplete array of items", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with additional items", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array", + "data": [ ], + "valid": true + }, + { + "description": "JavaScript pseudo-array is valid", + "data": { + "0": "invalid", + "1": "valid", + "length": 2 + }, + "valid": true + } + ] + }, + { + "description": "items with boolean schema (true)", + "schema": {"items": true}, + "tests": [ + { + "description": "any array is valid", + "data": [ 1, "foo", true ], + "valid": true + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schema (false)", + "schema": {"items": false}, + "tests": [ + { + "description": "any non-empty array is invalid", + "data": [ 1, "foo", true ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items with boolean schemas", + "schema": { + "items": [true, false] + }, + "tests": [ + { + "description": "array with one item is valid", + "data": [ 1 ], + "valid": true + }, + { + "description": "array with two items is invalid", + "data": [ 1, "foo" ], + "valid": false + }, + { + "description": "empty array is valid", + "data": [], + "valid": true + } + ] + }, + { + "description": "items and subitems", + "schema": { + "$defs": { + "item": { + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/$defs/sub-item" }, + { "$ref": "#/$defs/sub-item" } + ] + }, + "sub-item": { + "type": "object", + "required": ["foo"] + } + }, + "type": "array", + "additionalItems": false, + "items": [ + { "$ref": "#/$defs/item" }, + { "$ref": "#/$defs/item" }, + { "$ref": "#/$defs/item" } + ] + }, + "tests": [ + { + "description": "valid items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": true + }, + { + "description": "too many items", + "data": [ + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "too many sub-items", + "data": [ + [ {"foo": null}, {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong item", + "data": [ + {"foo": null}, + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "wrong sub-item", + "data": [ + [ {}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ], + [ {"foo": null}, {"foo": null} ] + ], + "valid": false + }, + { + "description": "fewer items is valid", + "data": [ + [ {"foo": null} ], + [ {"foo": null} ] + ], + "valid": true + } + ] + }, + { + "description": "nested items", + "schema": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + }, + "tests": [ + { + "description": "valid nested array", + "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": true + }, + { + "description": "nested array with invalid type", + "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], + "valid": false + }, + { + "description": "not deep enough", + "data": [[[1], [2],[3]], [[4], [5], [6]]], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/maxContains.json b/test/JSON-Schema-Test-Suite/tests/latest/maxContains.json new file mode 100755 index 00000000..3c42fb31 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/maxContains.json @@ -0,0 +1,79 @@ +[ + { + "description": "maxContains without contains is ignored", + "schema": { + "maxContains": 1 + }, + "tests": [ + { + "description": "one item valid against lone maxContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "two items still valid against lone maxContains", + "data": [ 1, 2 ], + "valid": true + } + ] + }, + { + "description": "maxContains with contains", + "schema": { + "contains": {"const": 1}, + "maxContains": 1 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, valid maxContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "all elements match, invalid maxContains", + "data": [ 1, 1 ], + "valid": false + }, + { + "description": "some elements match, valid maxContains", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "some elements match, invalid maxContains", + "data": [ 1, 2, 1 ], + "valid": false + } + ] + }, + { + "description": "minContains < maxContains", + "schema": { + "contains": {"const": 1}, + "minContains": 1, + "maxContains": 3 + }, + "tests": [ + { + "description": "actual < minContains < maxContains", + "data": [ ], + "valid": false + }, + { + "description": "minContains < actual < maxContains", + "data": [ 1, 1 ], + "valid": true + }, + { + "description": "minContains < maxContains < actual", + "data": [ 1, 1, 1, 1 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/maxItems.json b/test/JSON-Schema-Test-Suite/tests/latest/maxItems.json new file mode 100755 index 00000000..3b53a6b3 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/maxItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "maxItems validation", + "schema": {"maxItems": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": [1], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "too long is invalid", + "data": [1, 2, 3], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "foobar", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/maxLength.json b/test/JSON-Schema-Test-Suite/tests/latest/maxLength.json new file mode 100755 index 00000000..811d35b2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/maxLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "maxLength validation", + "schema": {"maxLength": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": "f", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too long is invalid", + "data": "foo", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 100, + "valid": true + }, + { + "description": "two supplementary Unicode code points is long enough", + "data": "\uD83D\uDCA9\uD83D\uDCA9", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/maxProperties.json b/test/JSON-Schema-Test-Suite/tests/latest/maxProperties.json new file mode 100755 index 00000000..aa7209f5 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/maxProperties.json @@ -0,0 +1,54 @@ +[ + { + "description": "maxProperties validation", + "schema": {"maxProperties": 2}, + "tests": [ + { + "description": "shorter is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "too long is invalid", + "data": {"foo": 1, "bar": 2, "baz": 3}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [1, 2, 3], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "maxProperties = 0 means the object is empty", + "schema": { "maxProperties": 0 }, + "tests": [ + { + "description": "no properties is valid", + "data": {}, + "valid": true + }, + { + "description": "one property is invalid", + "data": { "foo": 1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/maximum.json b/test/JSON-Schema-Test-Suite/tests/latest/maximum.json new file mode 100755 index 00000000..6844a39e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/maximum.json @@ -0,0 +1,54 @@ +[ + { + "description": "maximum validation", + "schema": {"maximum": 3.0}, + "tests": [ + { + "description": "below the maximum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 3.0, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 3.5, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "maximum validation with unsigned integer", + "schema": {"maximum": 300}, + "tests": [ + { + "description": "below the maximum is invalid", + "data": 299.97, + "valid": true + }, + { + "description": "boundary point integer is valid", + "data": 300, + "valid": true + }, + { + "description": "boundary point float is valid", + "data": 300.00, + "valid": true + }, + { + "description": "above the maximum is invalid", + "data": 300.5, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/minContains.json b/test/JSON-Schema-Test-Suite/tests/latest/minContains.json new file mode 100755 index 00000000..f359de0f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/minContains.json @@ -0,0 +1,172 @@ +[ + { + "description": "minContains without contains is ignored", + "schema": { + "minContains": 1 + }, + "tests": [ + { + "description": "one item valid against lone minContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "zero items still valid against lone minContains", + "data": [], + "valid": true + } + ] + }, + { + "description": "minContains=1 with contains", + "schema": { + "contains": {"const": 1}, + "minContains": 1 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "no elements match", + "data": [ 2 ], + "valid": false + }, + { + "description": "single element matches, valid minContains", + "data": [ 1 ], + "valid": true + }, + { + "description": "some elements match, valid minContains", + "data": [ 1, 2 ], + "valid": true + }, + { + "description": "all elements match, valid minContains", + "data": [ 1, 1 ], + "valid": true + } + ] + }, + { + "description": "minContains=2 with contains", + "schema": { + "contains": {"const": 1}, + "minContains": 2 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "some elements match, invalid minContains", + "data": [ 1, 2 ], + "valid": false + }, + { + "description": "all elements match, valid minContains (exactly as needed)", + "data": [ 1, 1 ], + "valid": true + }, + { + "description": "all elements match, valid minContains (more than needed)", + "data": [ 1, 1, 1 ], + "valid": true + }, + { + "description": "some elements match, valid minContains", + "data": [ 1, 2, 1 ], + "valid": true + } + ] + }, + { + "description": "maxContains = minContains", + "schema": { + "contains": {"const": 1}, + "maxContains": 2, + "minContains": 2 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "all elements match, invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "all elements match, invalid maxContains", + "data": [ 1, 1, 1 ], + "valid": false + }, + { + "description": "all elements match, valid maxContains and minContains", + "data": [ 1, 1 ], + "valid": true + } + ] + }, + { + "description": "maxContains < minContains", + "schema": { + "contains": {"const": 1}, + "maxContains": 1, + "minContains": 3 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": false + }, + { + "description": "invalid minContains", + "data": [ 1 ], + "valid": false + }, + { + "description": "invalid maxContains", + "data": [ 1, 1, 1 ], + "valid": false + }, + { + "description": "invalid maxContains and minContains", + "data": [ 1, 1 ], + "valid": false + } + ] + }, + { + "description": "minContains = 0", + "schema": { + "contains": {"const": 1}, + "minContains": 0 + }, + "tests": [ + { + "description": "empty data", + "data": [ ], + "valid": true + }, + { + "description": "minContains = 0 makes contains always pass", + "data": [ 2 ], + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/minItems.json b/test/JSON-Schema-Test-Suite/tests/latest/minItems.json new file mode 100755 index 00000000..ed511881 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/minItems.json @@ -0,0 +1,28 @@ +[ + { + "description": "minItems validation", + "schema": {"minItems": 1}, + "tests": [ + { + "description": "longer is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "exact length is valid", + "data": [1], + "valid": true + }, + { + "description": "too short is invalid", + "data": [], + "valid": false + }, + { + "description": "ignores non-arrays", + "data": "", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/minLength.json b/test/JSON-Schema-Test-Suite/tests/latest/minLength.json new file mode 100755 index 00000000..3f09158d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/minLength.json @@ -0,0 +1,33 @@ +[ + { + "description": "minLength validation", + "schema": {"minLength": 2}, + "tests": [ + { + "description": "longer is valid", + "data": "foo", + "valid": true + }, + { + "description": "exact length is valid", + "data": "fo", + "valid": true + }, + { + "description": "too short is invalid", + "data": "f", + "valid": false + }, + { + "description": "ignores non-strings", + "data": 1, + "valid": true + }, + { + "description": "one supplementary Unicode code point is not long enough", + "data": "\uD83D\uDCA9", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/minProperties.json b/test/JSON-Schema-Test-Suite/tests/latest/minProperties.json new file mode 100755 index 00000000..49a0726e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/minProperties.json @@ -0,0 +1,38 @@ +[ + { + "description": "minProperties validation", + "schema": {"minProperties": 1}, + "tests": [ + { + "description": "longer is valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "exact length is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "too short is invalid", + "data": {}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/minimum.json b/test/JSON-Schema-Test-Suite/tests/latest/minimum.json new file mode 100755 index 00000000..21ae50e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/minimum.json @@ -0,0 +1,69 @@ +[ + { + "description": "minimum validation", + "schema": {"minimum": 1.1}, + "tests": [ + { + "description": "above the minimum is valid", + "data": 2.6, + "valid": true + }, + { + "description": "boundary point is valid", + "data": 1.1, + "valid": true + }, + { + "description": "below the minimum is invalid", + "data": 0.6, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + }, + { + "description": "minimum validation with signed integer", + "schema": {"minimum": -2}, + "tests": [ + { + "description": "negative above the minimum is valid", + "data": -1, + "valid": true + }, + { + "description": "positive above the minimum is valid", + "data": 0, + "valid": true + }, + { + "description": "boundary point is valid", + "data": -2, + "valid": true + }, + { + "description": "boundary point with float is valid", + "data": -2.0, + "valid": true + }, + { + "description": "float below the minimum is invalid", + "data": -2.0001, + "valid": false + }, + { + "description": "int below the minimum is invalid", + "data": -3, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "x", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/multipleOf.json b/test/JSON-Schema-Test-Suite/tests/latest/multipleOf.json new file mode 100755 index 00000000..faa87cff --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/multipleOf.json @@ -0,0 +1,71 @@ +[ + { + "description": "by int", + "schema": {"multipleOf": 2}, + "tests": [ + { + "description": "int by int", + "data": 10, + "valid": true + }, + { + "description": "int by int fail", + "data": 7, + "valid": false + }, + { + "description": "ignores non-numbers", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "by number", + "schema": {"multipleOf": 1.5}, + "tests": [ + { + "description": "zero is multiple of anything", + "data": 0, + "valid": true + }, + { + "description": "4.5 is multiple of 1.5", + "data": 4.5, + "valid": true + }, + { + "description": "35 is not multiple of 1.5", + "data": 35, + "valid": false + } + ] + }, + { + "description": "by small number", + "schema": {"multipleOf": 0.0001}, + "tests": [ + { + "description": "0.0075 is multiple of 0.0001", + "data": 0.0075, + "valid": true + }, + { + "description": "0.00751 is not multiple of 0.0001", + "data": 0.00751, + "valid": false + } + ] + }, + { + "description": "invalid instance should not raise error when float division = inf", + "schema": {"type": "integer", "multipleOf": 0.123456789}, + "tests": [ + { + "description": "always invalid, but naive implementations may raise an overflow error", + "data": 1e308, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/not.json b/test/JSON-Schema-Test-Suite/tests/latest/not.json new file mode 100755 index 00000000..98de0eda --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/not.json @@ -0,0 +1,117 @@ +[ + { + "description": "not", + "schema": { + "not": {"type": "integer"} + }, + "tests": [ + { + "description": "allowed", + "data": "foo", + "valid": true + }, + { + "description": "disallowed", + "data": 1, + "valid": false + } + ] + }, + { + "description": "not multiple types", + "schema": { + "not": {"type": ["integer", "boolean"]} + }, + "tests": [ + { + "description": "valid", + "data": "foo", + "valid": true + }, + { + "description": "mismatch", + "data": 1, + "valid": false + }, + { + "description": "other mismatch", + "data": true, + "valid": false + } + ] + }, + { + "description": "not more complex schema", + "schema": { + "not": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + }, + "tests": [ + { + "description": "match", + "data": 1, + "valid": true + }, + { + "description": "other match", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "mismatch", + "data": {"foo": "bar"}, + "valid": false + } + ] + }, + { + "description": "forbidden property", + "schema": { + "properties": { + "foo": { + "not": {} + } + } + }, + "tests": [ + { + "description": "property present", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "property absent", + "data": {"bar": 1, "baz": 2}, + "valid": true + } + ] + }, + { + "description": "not with boolean schema true", + "schema": {"not": true}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "not with boolean schema false", + "schema": {"not": false}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/oneOf.json b/test/JSON-Schema-Test-Suite/tests/latest/oneOf.json new file mode 100755 index 00000000..eeb7ae86 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/oneOf.json @@ -0,0 +1,274 @@ +[ + { + "description": "oneOf", + "schema": { + "oneOf": [ + { + "type": "integer" + }, + { + "minimum": 2 + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": 1, + "valid": true + }, + { + "description": "second oneOf valid", + "data": 2.5, + "valid": true + }, + { + "description": "both oneOf valid", + "data": 3, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": 1.5, + "valid": false + } + ] + }, + { + "description": "oneOf with base schema", + "schema": { + "type": "string", + "oneOf" : [ + { + "minLength": 2 + }, + { + "maxLength": 4 + } + ] + }, + "tests": [ + { + "description": "mismatch base schema", + "data": 3, + "valid": false + }, + { + "description": "one oneOf valid", + "data": "foobar", + "valid": true + }, + { + "description": "both oneOf valid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all true", + "schema": {"oneOf": [true, true, true]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, one true", + "schema": {"oneOf": [true, false, false]}, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "oneOf with boolean schemas, more than one true", + "schema": {"oneOf": [true, true, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf with boolean schemas, all false", + "schema": {"oneOf": [false, false, false]}, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "oneOf complex types", + "schema": { + "oneOf": [ + { + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"] + }, + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid (complex)", + "data": {"bar": 2}, + "valid": true + }, + { + "description": "second oneOf valid (complex)", + "data": {"foo": "baz"}, + "valid": true + }, + { + "description": "both oneOf valid (complex)", + "data": {"foo": "baz", "bar": 2}, + "valid": false + }, + { + "description": "neither oneOf valid (complex)", + "data": {"foo": 2, "bar": "quux"}, + "valid": false + } + ] + }, + { + "description": "oneOf with empty schema", + "schema": { + "oneOf": [ + { "type": "number" }, + {} + ] + }, + "tests": [ + { + "description": "one valid - valid", + "data": "foo", + "valid": true + }, + { + "description": "both valid - invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "oneOf with required", + "schema": { + "type": "object", + "oneOf": [ + { "required": ["foo", "bar"] }, + { "required": ["foo", "baz"] } + ] + }, + "tests": [ + { + "description": "both invalid - invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "first valid - valid", + "data": {"foo": 1, "bar": 2}, + "valid": true + }, + { + "description": "second valid - valid", + "data": {"foo": 1, "baz": 3}, + "valid": true + }, + { + "description": "both valid - invalid", + "data": {"foo": 1, "bar": 2, "baz" : 3}, + "valid": false + } + ] + }, + { + "description": "oneOf with missing optional property", + "schema": { + "oneOf": [ + { + "properties": { + "bar": true, + "baz": true + }, + "required": ["bar"] + }, + { + "properties": { + "foo": true + }, + "required": ["foo"] + } + ] + }, + "tests": [ + { + "description": "first oneOf valid", + "data": {"bar": 8}, + "valid": true + }, + { + "description": "second oneOf valid", + "data": {"foo": "foo"}, + "valid": true + }, + { + "description": "both oneOf valid", + "data": {"foo": "foo", "bar": 8}, + "valid": false + }, + { + "description": "neither oneOf valid", + "data": {"baz": "quux"}, + "valid": false + } + ] + }, + { + "description": "nested oneOf, to check validation semantics", + "schema": { + "oneOf": [ + { + "oneOf": [ + { + "type": "null" + } + ] + } + ] + }, + "tests": [ + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "anything non-null is invalid", + "data": 123, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/bignum.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/bignum.json new file mode 100755 index 00000000..fac275e2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/bignum.json @@ -0,0 +1,105 @@ +[ + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a bignum is an integer", + "data": 12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a bignum is a number", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "integer", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "a negative bignum is an integer", + "data": -12345678910111213141516171819202122232425262728293031, + "valid": true + } + ] + }, + { + "description": "number", + "schema": {"type": "number"}, + "tests": [ + { + "description": "a negative bignum is a number", + "data": -98249283749234923498293171823948729348710298301928331, + "valid": true + } + ] + }, + { + "description": "string", + "schema": {"type": "string"}, + "tests": [ + { + "description": "a bignum is not a string", + "data": 98249283749234923498293171823948729348710298301928331, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"maximum": 18446744073709551615}, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision", + "schema": { + "exclusiveMaximum": 972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for high numbers", + "data": 972783798187987123879878123.188781371, + "valid": false + } + ] + }, + { + "description": "integer comparison", + "schema": {"minimum": -18446744073709551615}, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -18446744073709551600, + "valid": true + } + ] + }, + { + "description": "float comparison with high precision on negative numbers", + "schema": { + "exclusiveMinimum": -972783798187987123879878123.18878137 + }, + "tests": [ + { + "description": "comparison works for very negative numbers", + "data": -972783798187987123879878123.188781371, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/ecmascript-regex.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/ecmascript-regex.json new file mode 100755 index 00000000..6ed6cbe4 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/ecmascript-regex.json @@ -0,0 +1,292 @@ +[ + { + "description": "ECMA 262 regex $ does not match trailing newline", + "schema": { + "type": "string", + "pattern": "^abc$" + }, + "tests": [ + { + "description": "matches in Python, but should not in jsonschema", + "data": "abc\n", + "valid": false + }, + { + "description": "should match", + "data": "abc", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex converts \\t to horizontal tab", + "schema": { + "type": "string", + "pattern": "^\\t$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\t", + "valid": false + }, + { + "description": "matches", + "data": "\u0009", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and upper letter", + "schema": { + "type": "string", + "pattern": "^\\cC$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cC", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 regex escapes control codes with \\c and lower letter", + "schema": { + "type": "string", + "pattern": "^\\cc$" + }, + "tests": [ + { + "description": "does not match", + "data": "\\cc", + "valid": false + }, + { + "description": "matches", + "data": "\u0003", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\d matches ascii digits only", + "schema": { + "type": "string", + "pattern": "^\\d$" + }, + "tests": [ + { + "description": "ASCII zero matches", + "data": "0", + "valid": true + }, + { + "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)", + "data": "߀", + "valid": false + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) does not match", + "data": "\u07c0", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\D matches everything but ascii digits", + "schema": { + "type": "string", + "pattern": "^\\D$" + }, + "tests": [ + { + "description": "ASCII zero does not match", + "data": "0", + "valid": false + }, + { + "description": "NKO DIGIT ZERO matches (unlike e.g. Python)", + "data": "߀", + "valid": true + }, + { + "description": "NKO DIGIT ZERO (as \\u escape) matches", + "data": "\u07c0", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\w matches ascii letters only", + "schema": { + "type": "string", + "pattern": "^\\w$" + }, + "tests": [ + { + "description": "ASCII 'a' matches", + "data": "a", + "valid": true + }, + { + "description": "latin-1 e-acute does not match (unlike e.g. Python)", + "data": "é", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\W matches everything but ascii letters", + "schema": { + "type": "string", + "pattern": "^\\W$" + }, + "tests": [ + { + "description": "ASCII 'a' does not match", + "data": "a", + "valid": false + }, + { + "description": "latin-1 e-acute matches (unlike e.g. Python)", + "data": "é", + "valid": true + } + ] + }, + { + "description": "ECMA 262 \\s matches whitespace", + "schema": { + "type": "string", + "pattern": "^\\s$" + }, + "tests": [ + { + "description": "ASCII space matches", + "data": " ", + "valid": true + }, + { + "description": "Character tabulation matches", + "data": "\t", + "valid": true + }, + { + "description": "Line tabulation matches", + "data": "\u000b", + "valid": true + }, + { + "description": "Form feed matches", + "data": "\u000c", + "valid": true + }, + { + "description": "latin-1 non-breaking-space matches", + "data": "\u00a0", + "valid": true + }, + { + "description": "zero-width whitespace matches", + "data": "\ufeff", + "valid": true + }, + { + "description": "line feed matches (line terminator)", + "data": "\u000a", + "valid": true + }, + { + "description": "paragraph separator matches (line terminator)", + "data": "\u2029", + "valid": true + }, + { + "description": "EM SPACE matches (Space_Separator)", + "data": "\u2003", + "valid": true + }, + { + "description": "Non-whitespace control does not match", + "data": "\u0001", + "valid": false + }, + { + "description": "Non-whitespace does not match", + "data": "\u2013", + "valid": false + } + ] + }, + { + "description": "ECMA 262 \\S matches everything but whitespace", + "schema": { + "type": "string", + "pattern": "^\\S$" + }, + "tests": [ + { + "description": "ASCII space does not match", + "data": " ", + "valid": false + }, + { + "description": "Character tabulation does not match", + "data": "\t", + "valid": false + }, + { + "description": "Line tabulation does not match", + "data": "\u000b", + "valid": false + }, + { + "description": "Form feed does not match", + "data": "\u000c", + "valid": false + }, + { + "description": "latin-1 non-breaking-space does not match", + "data": "\u00a0", + "valid": false + }, + { + "description": "zero-width whitespace does not match", + "data": "\ufeff", + "valid": false + }, + { + "description": "line feed does not match (line terminator)", + "data": "\u000a", + "valid": false + }, + { + "description": "paragraph separator does not match (line terminator)", + "data": "\u2029", + "valid": false + }, + { + "description": "EM SPACE does not match (Space_Separator)", + "data": "\u2003", + "valid": false + }, + { + "description": "Non-whitespace control matches", + "data": "\u0001", + "valid": true + }, + { + "description": "Non-whitespace matches", + "data": "\u2013", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/float-overflow.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/float-overflow.json new file mode 100755 index 00000000..52ff9827 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/float-overflow.json @@ -0,0 +1,13 @@ +[ + { + "description": "all integers are multiples of 0.5, if overflow is handled", + "schema": {"type": "integer", "multipleOf": 0.5}, + "tests": [ + { + "description": "valid if optional overflow handling is implemented", + "data": 1e308, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date-time.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date-time.json new file mode 100755 index 00000000..900fcb7c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date-time.json @@ -0,0 +1,63 @@ +[ + { + "description": "validation of date-time strings", + "schema": {"format": "date-time"}, + "tests": [ + { + "description": "a valid date-time string", + "data": "1963-06-19T08:30:06.283185Z", + "valid": true + }, + { + "description": "a valid date-time string without second fraction", + "data": "1963-06-19T08:30:06Z", + "valid": true + }, + { + "description": "a valid date-time string with plus offset", + "data": "1937-01-01T12:00:27.87+00:20", + "valid": true + }, + { + "description": "a valid date-time string with minus offset", + "data": "1990-12-31T15:59:50.123-08:00", + "valid": true + }, + { + "description": "a invalid day in date-time string", + "data": "1990-02-31T15:59:60.123-08:00", + "valid": false + }, + { + "description": "an invalid offset in date-time string", + "data": "1990-12-31T15:59:60-24:00", + "valid": false + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963 08:30:06 PST", + "valid": false + }, + { + "description": "case-insensitive T and Z", + "data": "1963-06-19t08:30:06.283185z", + "valid": true + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350T01:01:01", + "valid": false + }, + { + "description": "invalid non-padded month dates", + "data": "1963-6-19T08:30:06.283185Z", + "valid": false + }, + { + "description": "invalid non-padded day dates", + "data": "1963-06-1T08:30:06.283185Z", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date.json new file mode 100755 index 00000000..453b51de --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/date.json @@ -0,0 +1,33 @@ +[ + { + "description": "validation of date strings", + "schema": {"format": "date"}, + "tests": [ + { + "description": "a valid date string", + "data": "1963-06-19", + "valid": true + }, + { + "description": "an invalid date-time string", + "data": "06/19/1963", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "2013-350", + "valid": false + }, + { + "description": "invalidates non-padded month dates", + "data": "1998-1-20", + "valid": false + }, + { + "description": "invalidates non-padded day dates", + "data": "1998-01-1", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/duration.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/duration.json new file mode 100755 index 00000000..45147381 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/duration.json @@ -0,0 +1,93 @@ +[ + { + "description": "validation of duration strings", + "schema": {"format": "duration"}, + "tests": [ + { + "description": "a valid duration string", + "data": "P4DT12H30M5S", + "valid": true + }, + { + "description": "an invalid duration string", + "data": "PT1D", + "valid": false + }, + { + "description": "no elements present", + "data": "P", + "valid": false + }, + { + "description": "no time elements present", + "data": "P1YT", + "valid": false + }, + { + "description": "no date or time elements present", + "data": "PT", + "valid": false + }, + { + "description": "elements out of order", + "data": "P2D1Y", + "valid": false + }, + { + "description": "missing time separator", + "data": "P1D2H", + "valid": false + }, + { + "description": "time element in the date position", + "data": "P2S", + "valid": false + }, + { + "description": "four years duration", + "data": "P4Y", + "valid": true + }, + { + "description": "zero time, in seconds", + "data": "PT0S", + "valid": true + }, + { + "description": "zero time, in days", + "data": "P0D", + "valid": true + }, + { + "description": "one month duration", + "data": "P1M", + "valid": true + }, + { + "description": "one minute duration", + "data": "PT1M", + "valid": true + }, + { + "description": "one and a half days, in hours", + "data": "PT36H", + "valid": true + }, + { + "description": "one and a half days, in days and hours", + "data": "P1DT12H", + "valid": true + }, + { + "description": "two weeks", + "data": "P2W", + "valid": true + }, + { + "description": "weeks cannot be combined with other units", + "data": "P1Y2W", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/email.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/email.json new file mode 100755 index 00000000..02396d26 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/email.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of e-mail addresses", + "schema": {"format": "email"}, + "tests": [ + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "tilde in local part is valid", + "data": "te~st@example.com", + "valid": true + }, + { + "description": "tilde before local part is valid", + "data": "~test@example.com", + "valid": true + }, + { + "description": "tilde after local part is valid", + "data": "test~@example.com", + "valid": true + }, + { + "description": "dot before local part is not valid", + "data": ".test@example.com", + "valid": false + }, + { + "description": "dot after local part is not valid", + "data": "test.@example.com", + "valid": false + }, + { + "description": "two separated dots inside local part are valid", + "data": "te.s.t@example.com", + "valid": true + }, + { + "description": "two subsequent dots inside local part are not valid", + "data": "te..st@example.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/hostname.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/hostname.json new file mode 100755 index 00000000..476541a8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/hostname.json @@ -0,0 +1,68 @@ +[ + { + "description": "validation of host names", + "schema": {"format": "hostname"}, + "tests": [ + { + "description": "a valid host name", + "data": "www.example.com", + "valid": true + }, + { + "description": "a valid punycoded IDN hostname", + "data": "xn--4gbwdl.xn--wgbh1c", + "valid": true + }, + { + "description": "a host name starting with an illegal character", + "data": "-a-host-name-that-starts-with--", + "valid": false + }, + { + "description": "a host name containing illegal characters", + "data": "not_a_valid_host_name", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", + "valid": false + }, + { + "description": "starts with hyphen", + "data": "-hostname", + "valid": false + }, + { + "description": "ends with hyphen", + "data": "hostname-", + "valid": false + }, + { + "description": "starts with underscore", + "data": "_hostname", + "valid": false + }, + { + "description": "ends with underscore", + "data": "hostname_", + "valid": false + }, + { + "description": "contains underscore", + "data": "host_name", + "valid": false + }, + { + "description": "maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com", + "valid": true + }, + { + "description": "exceeds maximum label length", + "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-email.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-email.json new file mode 100755 index 00000000..552d1067 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-email.json @@ -0,0 +1,28 @@ +[ + { + "description": "validation of an internationalized e-mail addresses", + "schema": {"format": "idn-email"}, + "tests": [ + { + "description": "a valid idn e-mail (example@example.test in Hangul)", + "data": "실례@실례.테스트", + "valid": true + }, + { + "description": "an invalid idn e-mail address", + "data": "2962", + "valid": false + }, + { + "description": "a valid e-mail address", + "data": "joe.bloggs@example.com", + "valid": true + }, + { + "description": "an invalid e-mail address", + "data": "2962", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-hostname.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-hostname.json new file mode 100755 index 00000000..bbb257b9 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/idn-hostname.json @@ -0,0 +1,274 @@ +[ + { + "description": "validation of internationalized host names", + "schema": { "format": "idn-hostname" }, + "tests": [ + { + "description": "a valid host name (example.test in Hangul)", + "data": "실례.테스트", + "valid": true + }, + { + "description": "illegal first char U+302E Hangul single dot tone mark", + "data": "〮실례.테스트", + "valid": false + }, + { + "description": "contains illegal char U+302E Hangul single dot tone mark", + "data": "실〮례.테스트", + "valid": false + }, + { + "description": "a host name with a component too long", + "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트", + "valid": false + }, + { + "description": "invalid label, correct Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1", + "data": "-> $1.00 <--", + "valid": false + }, + { + "description": "valid Chinese Punycode", + "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4", + "data": "xn--ihqwcrb4cv8a8dqg056pqjye", + "valid": true + }, + { + "description": "invalid Punycode", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "xn--X", + "valid": false + }, + { + "description": "U-label contains \"--\" in the 3rd and 4th position", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1", + "data": "XN--aa---o47jg78q", + "valid": false + }, + { + "description": "U-label starts with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello", + "valid": false + }, + { + "description": "U-label ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "hello-", + "valid": false + }, + { + "description": "U-label starts and ends with a dash", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1", + "data": "-hello-", + "valid": false + }, + { + "description": "Begins with a Spacing Combining Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0903hello", + "valid": false + }, + { + "description": "Begins with a Nonspacing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0300hello", + "valid": false + }, + { + "description": "Begins with an Enclosing Mark", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2", + "data": "\u0488hello", + "valid": false + }, + { + "description": "Exceptions that are PVALID, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u00df\u03c2\u0f0b\u3007", + "valid": true + }, + { + "description": "Exceptions that are PVALID, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u06fd\u06fe", + "valid": true + }, + { + "description": "Exceptions that are DISALLOWED, right-to-left chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6", + "data": "\u0640\u07fa", + "valid": false + }, + { + "description": "Exceptions that are DISALLOWED, left-to-right chars", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start", + "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b", + "valid": false + }, + { + "description": "MIDDLE DOT with no preceding 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "a\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing preceding", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "\u00b7l", + "valid": false + }, + { + "description": "MIDDLE DOT with no following 'l'", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7a", + "valid": false + }, + { + "description": "MIDDLE DOT with nothing following", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7", + "valid": false + }, + { + "description": "MIDDLE DOT with surrounding 'l's", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3", + "data": "l\u00b7l", + "valid": true + }, + { + "description": "Greek KERAIA not followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375S", + "valid": false + }, + { + "description": "Greek KERAIA not followed by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375", + "valid": false + }, + { + "description": "Greek KERAIA followed by Greek", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4", + "data": "\u03b1\u0375\u03b2", + "valid": true + }, + { + "description": "Hebrew GERESH not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "A\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05f3\u05d1", + "valid": false + }, + { + "description": "Hebrew GERESH preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5", + "data": "\u05d0\u05f3\u05d1", + "valid": true + }, + { + "description": "Hebrew GERSHAYIM not preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "A\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05f4\u05d1", + "valid": false + }, + { + "description": "Hebrew GERSHAYIM preceded by Hebrew", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6", + "data": "\u05d0\u05f4\u05d1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "def\u30fbabc", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with no other characters", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb", + "valid": false + }, + { + "description": "KATAKANA MIDDLE DOT with Hiragana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u3041", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Katakana", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u30a1", + "valid": true + }, + { + "description": "KATAKANA MIDDLE DOT with Han", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7", + "data": "\u30fb\u4e08", + "valid": true + }, + { + "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", + "valid": false + }, + { + "description": "Arabic-Indic digits not 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": "\u0628\u0660\u0628", + "valid": true + }, + { + "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9", + "data": "\u06f00", + "valid": true + }, + { + "description": "ZERO WIDTH JOINER not preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER not preceded by anything", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u200d\u0937", + "valid": false + }, + { + "description": "ZERO WIDTH JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf", + "data": "\u0915\u094d\u200d\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER preceded by Virama", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1", + "data": "\u0915\u094d\u200c\u0937", + "valid": true + }, + { + "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp", + "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement", + "data": "\u0628\u064a\u200c\u0628\u064a", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv4.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv4.json new file mode 100755 index 00000000..8b99b9fb --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv4.json @@ -0,0 +1,38 @@ +[ + { + "description": "validation of IP addresses", + "schema": {"format": "ipv4"}, + "tests": [ + { + "description": "a valid IP address", + "data": "192.168.0.1", + "valid": true + }, + { + "description": "an IP address with too many components", + "data": "127.0.0.0.1", + "valid": false + }, + { + "description": "an IP address with out-of-range values", + "data": "256.256.256.256", + "valid": false + }, + { + "description": "an IP address without 4 components", + "data": "127.0", + "valid": false + }, + { + "description": "an IP address as an integer", + "data": "0x7f000001", + "valid": false + }, + { + "description": "an IP address as an integer (decimal)", + "data": "2130706433", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv6.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv6.json new file mode 100755 index 00000000..2a08cb46 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/ipv6.json @@ -0,0 +1,153 @@ +[ + { + "description": "validation of IPv6 addresses", + "schema": {"format": "ipv6"}, + "tests": [ + { + "description": "a valid IPv6 address", + "data": "::1", + "valid": true + }, + { + "description": "an IPv6 address with out-of-range values", + "data": "12345::", + "valid": false + }, + { + "description": "an IPv6 address with too many components", + "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", + "valid": false + }, + { + "description": "an IPv6 address containing illegal characters", + "data": "::laptop", + "valid": false + }, + { + "description": "no digits is valid", + "data": "::", + "valid": true + }, + { + "description": "leading colons is valid", + "data": "::42:ff:1", + "valid": true + }, + { + "description": "trailing colons is valid", + "data": "d6::", + "valid": true + }, + { + "description": "missing leading octet is invalid", + "data": ":2:3:4:5:6:7:8", + "valid": false + }, + { + "description": "missing trailing octet is invalid", + "data": "1:2:3:4:5:6:7:", + "valid": false + }, + { + "description": "missing leading octet with omitted octets later", + "data": ":2:3:4::8", + "valid": false + }, + { + "description": "two sets of double colons is invalid", + "data": "1::d6::42", + "valid": false + }, + { + "description": "mixed format with the ipv4 section as decimal octets", + "data": "1::d6:192.168.0.1", + "valid": true + }, + { + "description": "mixed format with double colons between the sections", + "data": "1:2::192.168.0.1", + "valid": true + }, + { + "description": "mixed format with ipv4 section with octet out of range", + "data": "1::2:192.168.256.1", + "valid": false + }, + { + "description": "mixed format with ipv4 section with a hex octet", + "data": "1::2:192.168.ff.1", + "valid": false + }, + { + "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)", + "data": "::ffff:192.168.0.1", + "valid": true + }, + { + "description": "triple colons is invalid", + "data": "1:2:3:4:5:::8", + "valid": false + }, + { + "description": "8 octets", + "data": "1:2:3:4:5:6:7:8", + "valid": true + }, + { + "description": "insufficient octets without double colons", + "data": "1:2:3:4:5:6:7", + "valid": false + }, + { + "description": "no colons is invalid", + "data": "1", + "valid": false + }, + { + "description": "ipv4 is not ipv6", + "data": "127.0.0.1", + "valid": false + }, + { + "description": "ipv4 segment must have 4 octets", + "data": "1:2:3:4:1.2.3", + "valid": false + }, + { + "description": "leading whitespace is invalid", + "data": " ::1", + "valid": false + }, + { + "description": "trailing whitespace is invalid", + "data": "::1 ", + "valid": false + }, + { + "description": "netmask is not a part of ipv6 address", + "data": "fe80::/64", + "valid": false + }, + { + "description": "zone id is not a part of ipv6 address", + "data": "fe80::a%eth1", + "valid": false + }, + { + "description": "a long valid ipv6", + "data": "1000:1000:1000:1000:1000:1000:255.255.255.255", + "valid": true + }, + { + "description": "a long invalid ipv6, below length limit, first", + "data": "100:100:100:100:100:100:255.255.255.255.255", + "valid": false + }, + { + "description": "a long invalid ipv6, below length limit, second", + "data": "100:100:100:100:100:100:100:255.255.255.255", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri-reference.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri-reference.json new file mode 100755 index 00000000..1fd779c2 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of IRI References", + "schema": {"format": "iri-reference"}, + "tests": [ + { + "description": "a valid IRI", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid protocol-relative IRI Reference", + "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid relative IRI Reference", + "data": "/âππ", + "valid": true + }, + { + "description": "an invalid IRI Reference", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "a valid IRI Reference", + "data": "âππ", + "valid": true + }, + { + "description": "a valid IRI fragment", + "data": "#ƒrägmênt", + "valid": true + }, + { + "description": "an invalid IRI fragment", + "data": "#ƒräg\\mênt", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri.json new file mode 100755 index 00000000..ed54094c --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/iri.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of IRIs", + "schema": {"format": "iri"}, + "tests": [ + { + "description": "a valid IRI with anchor tag", + "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", + "valid": true + }, + { + "description": "a valid IRI with anchor tag and parantheses", + "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", + "valid": true + }, + { + "description": "a valid IRI with URL-encoded stuff", + "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid IRI with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid IRI based on IPv6", + "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]", + "valid": true + }, + { + "description": "an invalid IRI based on IPv6", + "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "valid": false + }, + { + "description": "an invalid relative IRI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid IRI", + "data": "\\\\WINDOWS\\filëßåré", + "valid": false + }, + { + "description": "an invalid IRI though valid IRI reference", + "data": "âππ", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/json-pointer.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/json-pointer.json new file mode 100755 index 00000000..65c2f064 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/json-pointer.json @@ -0,0 +1,168 @@ +[ + { + "description": "validation of JSON-pointers (JSON String Representation)", + "schema": {"format": "json-pointer"}, + "tests": [ + { + "description": "a valid JSON-pointer", + "data": "/foo/bar~0/baz~1/%a", + "valid": true + }, + { + "description": "not a valid JSON-pointer (~ not escaped)", + "data": "/foo/bar~", + "valid": false + }, + { + "description": "valid JSON-pointer with empty segment", + "data": "/foo//bar", + "valid": true + }, + { + "description": "valid JSON-pointer with the last empty segment", + "data": "/foo/bar/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #1", + "data": "", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #2", + "data": "/foo", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #3", + "data": "/foo/0", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #4", + "data": "/", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #5", + "data": "/a~1b", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #6", + "data": "/c%d", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #7", + "data": "/e^f", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #8", + "data": "/g|h", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #9", + "data": "/i\\j", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #10", + "data": "/k\"l", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #11", + "data": "/ ", + "valid": true + }, + { + "description": "valid JSON-pointer as stated in RFC 6901 #12", + "data": "/m~0n", + "valid": true + }, + { + "description": "valid JSON-pointer used adding to the last array position", + "data": "/foo/-", + "valid": true + }, + { + "description": "valid JSON-pointer (- used as object member name)", + "data": "/foo/-/bar", + "valid": true + }, + { + "description": "valid JSON-pointer (multiple escaped characters)", + "data": "/~1~0~0~1~1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #1", + "data": "/~1.1", + "valid": true + }, + { + "description": "valid JSON-pointer (escaped with fraction part) #2", + "data": "/~0.1", + "valid": true + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", + "data": "#", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", + "data": "#/", + "valid": false + }, + { + "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", + "data": "#a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #1", + "data": "/~0~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (some escaped, but not all) #2", + "data": "/~0/~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #1", + "data": "/~2", + "valid": false + }, + { + "description": "not a valid JSON-pointer (wrong escape character) #2", + "data": "/~-1", + "valid": false + }, + { + "description": "not a valid JSON-pointer (multiple characters not escaped)", + "data": "/~~", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", + "data": "a", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", + "data": "0", + "valid": false + }, + { + "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", + "data": "a/a", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/regex.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/regex.json new file mode 100755 index 00000000..d99d021e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/regex.json @@ -0,0 +1,18 @@ +[ + { + "description": "validation of regular expressions", + "schema": {"format": "regex"}, + "tests": [ + { + "description": "a valid regular expression", + "data": "([abc])+\\s+$", + "valid": true + }, + { + "description": "a regular expression with unclosed parens is invalid", + "data": "^(abc]", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/relative-json-pointer.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/relative-json-pointer.json new file mode 100755 index 00000000..22fb14e0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/relative-json-pointer.json @@ -0,0 +1,53 @@ +[ + { + "description": "validation of Relative JSON Pointers (RJP)", + "schema": {"format": "relative-json-pointer"}, + "tests": [ + { + "description": "a valid upwards RJP", + "data": "1", + "valid": true + }, + { + "description": "a valid downwards RJP", + "data": "0/foo/bar", + "valid": true + }, + { + "description": "a valid up and then down RJP, with array index", + "data": "2/0/baz/1/zip", + "valid": true + }, + { + "description": "a valid RJP taking the member or index name", + "data": "0#", + "valid": true + }, + { + "description": "an invalid RJP that is a valid JSON Pointer", + "data": "/foo/bar", + "valid": false + }, + { + "description": "negative prefix", + "data": "-1/foo/bar", + "valid": false + }, + { + "description": "## is not a valid json-pointer", + "data": "0##", + "valid": false + }, + { + "description": "zero cannot be followed by other digits, plus json-pointer", + "data": "01/a", + "valid": false + }, + { + "description": "zero cannot be followed by other digits, plus octothorpe", + "data": "01#", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/time.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/time.json new file mode 100755 index 00000000..4ec8a01a --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/time.json @@ -0,0 +1,23 @@ +[ + { + "description": "validation of time strings", + "schema": {"format": "time"}, + "tests": [ + { + "description": "a valid time string", + "data": "08:30:06.283185Z", + "valid": true + }, + { + "description": "an invalid time string", + "data": "08:30:06 PST", + "valid": false + }, + { + "description": "only RFC3339 not all of ISO 8601 are valid", + "data": "01:01:01,1111", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-reference.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-reference.json new file mode 100755 index 00000000..e4c9eef6 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-reference.json @@ -0,0 +1,43 @@ +[ + { + "description": "validation of URI References", + "schema": {"format": "uri-reference"}, + "tests": [ + { + "description": "a valid URI", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid relative URI Reference", + "data": "/abc", + "valid": true + }, + { + "description": "an invalid URI Reference", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "a valid URI Reference", + "data": "abc", + "valid": true + }, + { + "description": "a valid URI fragment", + "data": "#fragment", + "valid": true + }, + { + "description": "an invalid URI fragment", + "data": "#frag\\ment", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-template.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-template.json new file mode 100755 index 00000000..33ab76ee --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri-template.json @@ -0,0 +1,28 @@ +[ + { + "description": "format: uri-template", + "schema": {"format": "uri-template"}, + "tests": [ + { + "description": "a valid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term}", + "valid": true + }, + { + "description": "an invalid uri-template", + "data": "http://example.com/dictionary/{term:1}/{term", + "valid": false + }, + { + "description": "a valid uri-template without variables", + "data": "http://example.com/dictionary", + "valid": true + }, + { + "description": "a valid relative uri-template", + "data": "dictionary/{term:1}/{term}", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri.json new file mode 100755 index 00000000..4306a686 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uri.json @@ -0,0 +1,108 @@ +[ + { + "description": "validation of URIs", + "schema": {"format": "uri"}, + "tests": [ + { + "description": "a valid URL with anchor tag", + "data": "http://foo.bar/?baz=qux#quux", + "valid": true + }, + { + "description": "a valid URL with anchor tag and parantheses", + "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", + "valid": true + }, + { + "description": "a valid URL with URL-encoded stuff", + "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", + "valid": true + }, + { + "description": "a valid puny-coded URL ", + "data": "http://xn--nw2a.xn--j6w193g/", + "valid": true + }, + { + "description": "a valid URL with many special characters", + "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", + "valid": true + }, + { + "description": "a valid URL based on IPv4", + "data": "http://223.255.255.254", + "valid": true + }, + { + "description": "a valid URL with ftp scheme", + "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", + "valid": true + }, + { + "description": "a valid URL for a simple text file", + "data": "http://www.ietf.org/rfc/rfc2396.txt", + "valid": true + }, + { + "description": "a valid URL ", + "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", + "valid": true + }, + { + "description": "a valid mailto URI", + "data": "mailto:John.Doe@example.com", + "valid": true + }, + { + "description": "a valid newsgroup URI", + "data": "news:comp.infosystems.www.servers.unix", + "valid": true + }, + { + "description": "a valid tel URI", + "data": "tel:+1-816-555-1212", + "valid": true + }, + { + "description": "a valid URN", + "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", + "valid": true + }, + { + "description": "an invalid protocol-relative URI Reference", + "data": "//foo.bar/?baz=qux#quux", + "valid": false + }, + { + "description": "an invalid relative URI Reference", + "data": "/abc", + "valid": false + }, + { + "description": "an invalid URI", + "data": "\\\\WINDOWS\\fileshare", + "valid": false + }, + { + "description": "an invalid URI though valid URI reference", + "data": "abc", + "valid": false + }, + { + "description": "an invalid URI with spaces", + "data": "http:// shouldfail.com", + "valid": false + }, + { + "description": "an invalid URI with spaces and missing scheme", + "data": ":// should fail", + "valid": false + }, + { + "description": "an invalid URI with comma in scheme", + "data": "bar,baz:foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uuid.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uuid.json new file mode 100755 index 00000000..45bf349b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/format/uuid.json @@ -0,0 +1,70 @@ +[ + { + "description": "uuid format", + "schema": { + "format": "uuid" + }, + "tests": [ + { + "description": "all upper-case", + "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380", + "valid": true + }, + { + "description": "all lower-case", + "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380", + "valid": true + }, + { + "description": "mixed case", + "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380", + "valid": true + }, + { + "description": "all zeroes is valid", + "data": "00000000-0000-0000-0000-000000000000", + "valid": true + }, + { + "description": "wrong length", + "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638", + "valid": false + }, + { + "description": "missing section", + "data": "2eb8aa08-aa98-11ea-73b441d16380", + "valid": false + }, + { + "description": "bad characters (not hex)", + "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380", + "valid": false + }, + { + "description": "no dashes", + "data": "2eb8aa08aa9811eab4aa73b441d16380", + "valid": false + }, + { + "description": "valid version 4", + "data": "98d80576-482e-427f-8434-7f86890ab222", + "valid": true + }, + { + "description": "valid version 5", + "data": "99c17cbb-656f-564a-940f-1a4568f03487", + "valid": true + }, + { + "description": "hypothetical version 6", + "data": "99c17cbb-656f-664a-940f-1a4568f03487", + "valid": true + }, + { + "description": "hypothetical version 15", + "data": "99c17cbb-656f-f64a-940f-1a4568f03487", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/non-bmp-regex.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/non-bmp-regex.json new file mode 100755 index 00000000..dd67af2b --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/non-bmp-regex.json @@ -0,0 +1,82 @@ +[ + { + "description": "Proper UTF-16 surrogate pair handling: pattern", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { "pattern": "^🐲*$" }, + "tests": [ + { + "description": "matches empty", + "data": "", + "valid": true + }, + { + "description": "matches single", + "data": "🐲", + "valid": true + }, + { + "description": "matches two", + "data": "🐲🐲", + "valid": true + }, + { + "description": "doesn't match one", + "data": "🐉", + "valid": false + }, + { + "description": "doesn't match two", + "data": "🐉🐉", + "valid": false + }, + { + "description": "doesn't match one ASCII", + "data": "D", + "valid": false + }, + { + "description": "doesn't match two ASCII", + "data": "DD", + "valid": false + } + ] + }, + { + "description": "Proper UTF-16 surrogate pair handling: patternProperties", + "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters", + "schema": { + "patternProperties": { + "^🐲*$": { + "type": "integer" + } + } + }, + "tests": [ + { + "description": "matches empty", + "data": { "": 1 }, + "valid": true + }, + { + "description": "matches single", + "data": { "🐲": 1 }, + "valid": true + }, + { + "description": "matches two", + "data": { "🐲🐲": 1 }, + "valid": true + }, + { + "description": "doesn't match one", + "data": { "🐲": "hello" }, + "valid": false + }, + { + "description": "doesn't match two", + "data": { "🐲🐲": "hello" }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/optional/refOfUnknownKeyword.json b/test/JSON-Schema-Test-Suite/tests/latest/optional/refOfUnknownKeyword.json new file mode 100755 index 00000000..5b150df8 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/optional/refOfUnknownKeyword.json @@ -0,0 +1,44 @@ +[ + { + "description": "reference of a root arbitrary keyword ", + "schema": { + "unknown-keyword": {"type": "integer"}, + "properties": { + "bar": {"$ref": "#/unknown-keyword"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "reference of an arbitrary keyword of a sub-schema", + "schema": { + "properties": { + "foo": {"unknown-keyword": {"type": "integer"}}, + "bar": {"$ref": "#/properties/foo/unknown-keyword"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/pattern.json b/test/JSON-Schema-Test-Suite/tests/latest/pattern.json new file mode 100755 index 00000000..92db0f97 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/pattern.json @@ -0,0 +1,59 @@ +[ + { + "description": "pattern validation", + "schema": {"pattern": "^a*$"}, + "tests": [ + { + "description": "a matching pattern is valid", + "data": "aaa", + "valid": true + }, + { + "description": "a non-matching pattern is invalid", + "data": "abc", + "valid": false + }, + { + "description": "ignores booleans", + "data": true, + "valid": true + }, + { + "description": "ignores integers", + "data": 123, + "valid": true + }, + { + "description": "ignores floats", + "data": 1.0, + "valid": true + }, + { + "description": "ignores objects", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores null", + "data": null, + "valid": true + } + ] + }, + { + "description": "pattern is not anchored", + "schema": {"pattern": "a+"}, + "tests": [ + { + "description": "matches a substring", + "data": "xxaayy", + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/patternProperties.json b/test/JSON-Schema-Test-Suite/tests/latest/patternProperties.json new file mode 100755 index 00000000..c10ffcc0 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/patternProperties.json @@ -0,0 +1,156 @@ +[ + { + "description": + "patternProperties validates properties matching a regex", + "schema": { + "patternProperties": { + "f.*o": {"type": "integer"} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "multiple valid matches is valid", + "data": {"foo": 1, "foooooo" : 2}, + "valid": true + }, + { + "description": "a single invalid match is invalid", + "data": {"foo": "bar", "fooooo": 2}, + "valid": false + }, + { + "description": "multiple invalid matches is invalid", + "data": {"foo": "bar", "foooooo" : "baz"}, + "valid": false + }, + { + "description": "ignores arrays", + "data": ["foo"], + "valid": true + }, + { + "description": "ignores strings", + "data": "foo", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "multiple simultaneous patternProperties are validated", + "schema": { + "patternProperties": { + "a*": {"type": "integer"}, + "aaa*": {"maximum": 20} + } + }, + "tests": [ + { + "description": "a single valid match is valid", + "data": {"a": 21}, + "valid": true + }, + { + "description": "a simultaneous match is valid", + "data": {"aaaa": 18}, + "valid": true + }, + { + "description": "multiple matches is valid", + "data": {"a": 21, "aaaa": 18}, + "valid": true + }, + { + "description": "an invalid due to one is invalid", + "data": {"a": "bar"}, + "valid": false + }, + { + "description": "an invalid due to the other is invalid", + "data": {"aaaa": 31}, + "valid": false + }, + { + "description": "an invalid due to both is invalid", + "data": {"aaa": "foo", "aaaa": 31}, + "valid": false + } + ] + }, + { + "description": "regexes are not anchored by default and are case sensitive", + "schema": { + "patternProperties": { + "[0-9]{2,}": { "type": "boolean" }, + "X_": { "type": "string" } + } + }, + "tests": [ + { + "description": "non recognized members are ignored", + "data": { "answer 1": "42" }, + "valid": true + }, + { + "description": "recognized members are accounted for", + "data": { "a31b": null }, + "valid": false + }, + { + "description": "regexes are case sensitive", + "data": { "a_x_3": 3 }, + "valid": true + }, + { + "description": "regexes are case sensitive, 2", + "data": { "a_X_3": 3 }, + "valid": false + } + ] + }, + { + "description": "patternProperties with boolean schemas", + "schema": { + "patternProperties": { + "f.*": true, + "b.*": false + } + }, + "tests": [ + { + "description": "object with property matching schema true is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "object with property matching schema false is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "object with both properties is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + }, + { + "description": "object with a property matching both true and false is invalid", + "data": {"foobar":1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/properties.json b/test/JSON-Schema-Test-Suite/tests/latest/properties.json new file mode 100755 index 00000000..b86c1819 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/properties.json @@ -0,0 +1,167 @@ +[ + { + "description": "object properties validation", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "string"} + } + }, + "tests": [ + { + "description": "both properties present and valid is valid", + "data": {"foo": 1, "bar": "baz"}, + "valid": true + }, + { + "description": "one property invalid is invalid", + "data": {"foo": 1, "bar": {}}, + "valid": false + }, + { + "description": "both properties invalid is invalid", + "data": {"foo": [], "bar": {}}, + "valid": false + }, + { + "description": "doesn't invalidate other properties", + "data": {"quux": []}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": + "properties, patternProperties, additionalProperties interaction", + "schema": { + "properties": { + "foo": {"type": "array", "maxItems": 3}, + "bar": {"type": "array"} + }, + "patternProperties": {"f.o": {"minItems": 2}}, + "additionalProperties": {"type": "integer"} + }, + "tests": [ + { + "description": "property validates property", + "data": {"foo": [1, 2]}, + "valid": true + }, + { + "description": "property invalidates property", + "data": {"foo": [1, 2, 3, 4]}, + "valid": false + }, + { + "description": "patternProperty invalidates property", + "data": {"foo": []}, + "valid": false + }, + { + "description": "patternProperty validates nonproperty", + "data": {"fxo": [1, 2]}, + "valid": true + }, + { + "description": "patternProperty invalidates nonproperty", + "data": {"fxo": []}, + "valid": false + }, + { + "description": "additionalProperty ignores property", + "data": {"bar": []}, + "valid": true + }, + { + "description": "additionalProperty validates others", + "data": {"quux": 3}, + "valid": true + }, + { + "description": "additionalProperty invalidates others", + "data": {"quux": "foo"}, + "valid": false + } + ] + }, + { + "description": "properties with boolean schema", + "schema": { + "properties": { + "foo": true, + "bar": false + } + }, + "tests": [ + { + "description": "no property present is valid", + "data": {}, + "valid": true + }, + { + "description": "only 'true' property present is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "only 'false' property present is invalid", + "data": {"bar": 2}, + "valid": false + }, + { + "description": "both properties present is invalid", + "data": {"foo": 1, "bar": 2}, + "valid": false + } + ] + }, + { + "description": "properties with escaped characters", + "schema": { + "properties": { + "foo\nbar": {"type": "number"}, + "foo\"bar": {"type": "number"}, + "foo\\bar": {"type": "number"}, + "foo\rbar": {"type": "number"}, + "foo\tbar": {"type": "number"}, + "foo\fbar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with all numbers is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1", + "foo\\bar": "1", + "foo\rbar": "1", + "foo\tbar": "1", + "foo\fbar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/propertyNames.json b/test/JSON-Schema-Test-Suite/tests/latest/propertyNames.json new file mode 100755 index 00000000..8423690d --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/propertyNames.json @@ -0,0 +1,78 @@ +[ + { + "description": "propertyNames validation", + "schema": { + "propertyNames": {"maxLength": 3} + }, + "tests": [ + { + "description": "all property names valid", + "data": { + "f": {}, + "foo": {} + }, + "valid": true + }, + { + "description": "some property names invalid", + "data": { + "foo": {}, + "foobar": {} + }, + "valid": false + }, + { + "description": "object without properties is valid", + "data": {}, + "valid": true + }, + { + "description": "ignores arrays", + "data": [1, 2, 3, 4], + "valid": true + }, + { + "description": "ignores strings", + "data": "foobar", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema true", + "schema": {"propertyNames": true}, + "tests": [ + { + "description": "object with any properties is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + }, + { + "description": "propertyNames with boolean schema false", + "schema": {"propertyNames": false}, + "tests": [ + { + "description": "object with any properties is invalid", + "data": {"foo": 1}, + "valid": false + }, + { + "description": "empty object is valid", + "data": {}, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/recursiveRef.json b/test/JSON-Schema-Test-Suite/tests/latest/recursiveRef.json new file mode 100755 index 00000000..1a221ec1 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/recursiveRef.json @@ -0,0 +1,356 @@ +[ + { + "description": "$recursiveRef without $recursiveAnchor works like $ref", + "schema": { + "properties": { + "foo": { "$recursiveRef": "#" } + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": { "foo": { "foo": false } }, + "valid": true + }, + { + "description": "mismatch", + "data": { "bar": false }, + "valid": false + }, + { + "description": "recursive mismatch", + "data": { "foo": { "bar": false } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef without using nesting", + "schema": { + "$id": "http://localhost:4242/recursiveRef2/schema.json", + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, no match", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with nesting", + "schema": { + "$id": "http://localhost:4242/recursiveRef3/schema.json", + "$recursiveAnchor": true, + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer now matches as a property value", + "data": { "foo": 1 }, + "valid": true + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, properties match with $recursiveRef", + "data": { "foo": { "bar": 1 } }, + "valid": true + } + ] + }, + { + "description": "$recursiveRef with $recursiveAnchor: false works like $ref", + "schema": { + "$id": "http://localhost:4242/recursiveRef4/schema.json", + "$recursiveAnchor": false, + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": false, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, integer does not match as a property value", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor works like $ref", + "schema": { + "$id": "http://localhost:4242/recursiveRef5/schema.json", + "$defs": { + "myobject": { + "$id": "myobject.json", + "$recursiveAnchor": false, + "anyOf": [ + { "type": "string" }, + { + "type": "object", + "additionalProperties": { "$recursiveRef": "#" } + } + ] + } + }, + "anyOf": [ + { "type": "integer" }, + { "$ref": "#/$defs/myobject" } + ] + }, + "tests": [ + { + "description": "integer matches at the outer level", + "data": 1, + "valid": true + }, + { + "description": "single level match", + "data": { "foo": "hi" }, + "valid": true + }, + { + "description": "integer does not match as a property value", + "data": { "foo": 1 }, + "valid": false + }, + { + "description": "two levels, properties match with inner definition", + "data": { "foo": { "bar": "hi" } }, + "valid": true + }, + { + "description": "two levels, integer does not match as a property value", + "data": { "foo": { "bar": 1 } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor in the initial target schema resource", + "schema": { + "$id": "http://localhost:4242/recursiveRef6/base.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "boolean" }, + { + "type": "object", + "additionalProperties": { + "$id": "http://localhost:4242/recursiveRef6/inner.json", + "$comment": "there is no $recursiveAnchor: true here, so we do NOT recurse to the base", + "anyOf": [ + { "type": "integer" }, + { "type": "object", "additionalProperties": { "$recursiveRef": "#" } } + ] + } + } + ] + }, + "tests": [ + { + "description": "leaf node does not match; no recursion", + "data": { "foo": true }, + "valid": false + }, + { + "description": "leaf node matches: recursion uses the inner schema", + "data": { "foo": { "bar": 1 } }, + "valid": true + }, + { + "description": "leaf node does not match: recursion uses the inner schema", + "data": { "foo": { "bar": true } }, + "valid": false + } + ] + }, + { + "description": "$recursiveRef with no $recursiveAnchor in the outer schema resource", + "schema": { + "$id": "http://localhost:4242/recursiveRef7/base.json", + "anyOf": [ + { "type": "boolean" }, + { + "type": "object", + "additionalProperties": { + "$id": "http://localhost:4242/recursiveRef7/inner.json", + "$recursiveAnchor": true, + "anyOf": [ + { "type": "integer" }, + { "type": "object", "additionalProperties": { "$recursiveRef": "#" } } + ] + } + } + ] + }, + "tests": [ + { + "description": "leaf node does not match; no recursion", + "data": { "foo": true }, + "valid": false + }, + { + "description": "leaf node matches: recursion only uses inner schema", + "data": { "foo": { "bar": 1 } }, + "valid": true + }, + { + "description": "leaf node does not match: recursion only uses inner schema", + "data": { "foo": { "bar": true } }, + "valid": false + } + ] + }, + { + "description": "multiple dynamic paths to the $recursiveRef keyword", + "schema": { + "$id": "recursiveRef8_main.json", + "$defs": { + "inner": { + "$id": "recursiveRef8_inner.json", + "$recursiveAnchor": true, + "title": "inner", + "additionalProperties": { + "$recursiveRef": "#" + } + } + }, + "if": { + "propertyNames": { + "pattern": "^[a-m]" + } + }, + "then": { + "title": "any type of node", + "$id": "recursiveRef8_anyLeafNode.json", + "$recursiveAnchor": true, + "$ref": "recursiveRef8_main.json#/$defs/inner" + }, + "else": { + "title": "integer node", + "$id": "recursiveRef8_integerNode.json", + "$recursiveAnchor": true, + "type": [ "object", "integer" ], + "$ref": "recursiveRef8_main.json#/$defs/inner" + } + }, + "tests": [ + { + "description": "recurse to anyLeafNode - floats are allowed", + "data": { "alpha": 1.1 }, + "valid": true + }, + { + "description": "recurse to integerNode - floats are not allowed", + "data": { "november": 1.1 }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/ref.json b/test/JSON-Schema-Test-Suite/tests/latest/ref.json new file mode 100755 index 00000000..2da81e35 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/ref.json @@ -0,0 +1,434 @@ +[ + { + "description": "root pointer ref", + "schema": { + "properties": { + "foo": {"$ref": "#"} + }, + "additionalProperties": false + }, + "tests": [ + { + "description": "match", + "data": {"foo": false}, + "valid": true + }, + { + "description": "recursive match", + "data": {"foo": {"foo": false}}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": false}, + "valid": false + }, + { + "description": "recursive mismatch", + "data": {"foo": {"bar": false}}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to object", + "schema": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"$ref": "#/properties/foo"} + } + }, + "tests": [ + { + "description": "match", + "data": {"bar": 3}, + "valid": true + }, + { + "description": "mismatch", + "data": {"bar": true}, + "valid": false + } + ] + }, + { + "description": "relative pointer ref to array", + "schema": { + "items": [ + {"type": "integer"}, + {"$ref": "#/items/0"} + ] + }, + "tests": [ + { + "description": "match array", + "data": [1, 2], + "valid": true + }, + { + "description": "mismatch array", + "data": [1, "foo"], + "valid": false + } + ] + }, + { + "description": "escaped pointer ref", + "schema": { + "$defs": { + "tilde~field": {"type": "integer"}, + "slash/field": {"type": "integer"}, + "percent%field": {"type": "integer"} + }, + "properties": { + "tilde": {"$ref": "#/$defs/tilde~0field"}, + "slash": {"$ref": "#/$defs/slash~1field"}, + "percent": {"$ref": "#/$defs/percent%25field"} + } + }, + "tests": [ + { + "description": "slash invalid", + "data": {"slash": "aoeu"}, + "valid": false + }, + { + "description": "tilde invalid", + "data": {"tilde": "aoeu"}, + "valid": false + }, + { + "description": "percent invalid", + "data": {"percent": "aoeu"}, + "valid": false + }, + { + "description": "slash valid", + "data": {"slash": 123}, + "valid": true + }, + { + "description": "tilde valid", + "data": {"tilde": 123}, + "valid": true + }, + { + "description": "percent valid", + "data": {"percent": 123}, + "valid": true + } + ] + }, + { + "description": "nested refs", + "schema": { + "$defs": { + "a": {"type": "integer"}, + "b": {"$ref": "#/$defs/a"}, + "c": {"$ref": "#/$defs/b"} + }, + "$ref": "#/$defs/c" + }, + "tests": [ + { + "description": "nested ref valid", + "data": 5, + "valid": true + }, + { + "description": "nested ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref applies alongside sibling keywords", + "schema": { + "$defs": { + "reffed": { + "type": "array" + } + }, + "properties": { + "foo": { + "$ref": "#/$defs/reffed", + "maxItems": 2 + } + } + }, + "tests": [ + { + "description": "ref valid, maxItems valid", + "data": { "foo": [] }, + "valid": true + }, + { + "description": "ref valid, maxItems invalid", + "data": { "foo": [1, 2, 3] }, + "valid": false + }, + { + "description": "ref invalid", + "data": { "foo": "string" }, + "valid": false + } + ] + }, + { + "description": "remote ref, containing refs itself", + "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"}, + "tests": [ + { + "description": "remote ref valid", + "data": {"minLength": 1}, + "valid": true + }, + { + "description": "remote ref invalid", + "data": {"minLength": -1}, + "valid": false + } + ] + }, + { + "description": "property named $ref that is not a reference", + "schema": { + "properties": { + "$ref": {"type": "string"} + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "property named $ref, containing an actual $ref", + "schema": { + "properties": { + "$ref": {"$ref": "#/$defs/is-string"} + }, + "$defs": { + "is-string": { + "type": "string" + } + } + }, + "tests": [ + { + "description": "property named $ref valid", + "data": {"$ref": "a"}, + "valid": true + }, + { + "description": "property named $ref invalid", + "data": {"$ref": 2}, + "valid": false + } + ] + }, + { + "description": "$ref to boolean schema true", + "schema": { + "$ref": "#/$defs/bool", + "$defs": { + "bool": true + } + }, + "tests": [ + { + "description": "any value is valid", + "data": "foo", + "valid": true + } + ] + }, + { + "description": "$ref to boolean schema false", + "schema": { + "$ref": "#/$defs/bool", + "$defs": { + "bool": false + } + }, + "tests": [ + { + "description": "any value is invalid", + "data": "foo", + "valid": false + } + ] + }, + { + "description": "Recursive references between schemas", + "schema": { + "$id": "http://localhost:1234/tree", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"$ref": "node"} + } + }, + "required": ["meta", "nodes"], + "$defs": { + "node": { + "$id": "http://localhost:1234/node", + "description": "node", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"$ref": "tree"} + }, + "required": ["value"] + } + } + }, + "tests": [ + { + "description": "valid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 1.1}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": true + }, + { + "description": "invalid tree", + "data": { + "meta": "root", + "nodes": [ + { + "value": 1, + "subtree": { + "meta": "child", + "nodes": [ + {"value": "string is invalid"}, + {"value": 1.2} + ] + } + }, + { + "value": 2, + "subtree": { + "meta": "child", + "nodes": [ + {"value": 2.1}, + {"value": 2.2} + ] + } + } + ] + }, + "valid": false + } + ] + }, + { + "description": "refs with quote", + "schema": { + "properties": { + "foo\"bar": {"$ref": "#/$defs/foo%22bar"} + }, + "$defs": { + "foo\"bar": {"type": "number"} + } + }, + "tests": [ + { + "description": "object with numbers is valid", + "data": { + "foo\"bar": 1 + }, + "valid": true + }, + { + "description": "object with strings is invalid", + "data": { + "foo\"bar": "1" + }, + "valid": false + } + ] + }, + { + "description": "ref creates new scope when adjacent to keywords", + "schema": { + "$defs": { + "A": { + "unevaluatedProperties": false + } + }, + "properties": { + "prop1": { + "type": "string" + } + }, + "$ref": "#/$defs/A" + }, + "tests": [ + { + "description": "referenced subschema doesn't see annotations from properties", + "data": { + "prop1": "match" + }, + "valid": false + } + ] + }, + { + "description": "naive replacement of $ref with its destination is not correct", + "schema": { + "$defs": { + "a_string": { "type": "string" } + }, + "enum": [ + { "$ref": "#/$defs/a_string" } + ] + }, + "tests": [ + { + "description": "do not evaluate the $ref inside the enum", + "data": "this is a string", + "valid": false + }, + { + "description": "match the enum exactly", + "data": { "$ref": "#/$defs/a_string" }, + "valid": true + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/refRemote.json b/test/JSON-Schema-Test-Suite/tests/latest/refRemote.json new file mode 100755 index 00000000..b9c6a282 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/refRemote.json @@ -0,0 +1,167 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "http://localhost:1234/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/refToInteger" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "base URI change", + "schema": { + "$id": "http://localhost:1234/", + "items": { + "$id": "baseUriChange/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "base URI change ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "base URI change ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "base URI change - change folder", + "schema": { + "$id": "http://localhost:1234/scope_change_defs1.json", + "type" : "object", + "properties": {"list": {"$ref": "baseUriChangeFolder/"}}, + "$defs": { + "baz": { + "$id": "baseUriChangeFolder/", + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "base URI change - change folder in subschema", + "schema": { + "$id": "http://localhost:1234/scope_change_defs2.json", + "type" : "object", + "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}}, + "$defs": { + "baz": { + "$id": "baseUriChangeFolderInSubschema/", + "$defs": { + "bar": { + "type": "array", + "items": {"$ref": "folderInteger.json"} + } + } + } + } + }, + "tests": [ + { + "description": "number is valid", + "data": {"list": [1]}, + "valid": true + }, + { + "description": "string is invalid", + "data": {"list": ["a"]}, + "valid": false + } + ] + }, + { + "description": "root ref in remote ref", + "schema": { + "$id": "http://localhost:1234/object", + "type": "object", + "properties": { + "name": {"$ref": "name-defs.json#/$defs/orNull"} + } + }, + "tests": [ + { + "description": "string is valid", + "data": { + "name": "foo" + }, + "valid": true + }, + { + "description": "null is valid", + "data": { + "name": null + }, + "valid": true + }, + { + "description": "object is invalid", + "data": { + "name": { + "name": null + } + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/required.json b/test/JSON-Schema-Test-Suite/tests/latest/required.json new file mode 100755 index 00000000..abf18f34 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/required.json @@ -0,0 +1,105 @@ +[ + { + "description": "required validation", + "schema": { + "properties": { + "foo": {}, + "bar": {} + }, + "required": ["foo"] + }, + "tests": [ + { + "description": "present required property is valid", + "data": {"foo": 1}, + "valid": true + }, + { + "description": "non-present required property is invalid", + "data": {"bar": 1}, + "valid": false + }, + { + "description": "ignores arrays", + "data": [], + "valid": true + }, + { + "description": "ignores strings", + "data": "", + "valid": true + }, + { + "description": "ignores other non-objects", + "data": 12, + "valid": true + } + ] + }, + { + "description": "required default validation", + "schema": { + "properties": { + "foo": {} + } + }, + "tests": [ + { + "description": "not required by default", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with empty array", + "schema": { + "properties": { + "foo": {} + }, + "required": [] + }, + "tests": [ + { + "description": "property not required", + "data": {}, + "valid": true + } + ] + }, + { + "description": "required with escaped characters", + "schema": { + "required": [ + "foo\nbar", + "foo\"bar", + "foo\\bar", + "foo\rbar", + "foo\tbar", + "foo\fbar" + ] + }, + "tests": [ + { + "description": "object with all properties present is valid", + "data": { + "foo\nbar": 1, + "foo\"bar": 1, + "foo\\bar": 1, + "foo\rbar": 1, + "foo\tbar": 1, + "foo\fbar": 1 + }, + "valid": true + }, + { + "description": "object with some properties missing is invalid", + "data": { + "foo\nbar": "1", + "foo\"bar": "1" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/type.json b/test/JSON-Schema-Test-Suite/tests/latest/type.json new file mode 100755 index 00000000..83046470 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/type.json @@ -0,0 +1,474 @@ +[ + { + "description": "integer type matches integers", + "schema": {"type": "integer"}, + "tests": [ + { + "description": "an integer is an integer", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is an integer", + "data": 1.0, + "valid": true + }, + { + "description": "a float is not an integer", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an integer", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not an integer, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not an integer", + "data": {}, + "valid": false + }, + { + "description": "an array is not an integer", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an integer", + "data": true, + "valid": false + }, + { + "description": "null is not an integer", + "data": null, + "valid": false + } + ] + }, + { + "description": "number type matches numbers", + "schema": {"type": "number"}, + "tests": [ + { + "description": "an integer is a number", + "data": 1, + "valid": true + }, + { + "description": "a float with zero fractional part is a number (and an integer)", + "data": 1.0, + "valid": true + }, + { + "description": "a float is a number", + "data": 1.1, + "valid": true + }, + { + "description": "a string is not a number", + "data": "foo", + "valid": false + }, + { + "description": "a string is still not a number, even if it looks like one", + "data": "1", + "valid": false + }, + { + "description": "an object is not a number", + "data": {}, + "valid": false + }, + { + "description": "an array is not a number", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a number", + "data": true, + "valid": false + }, + { + "description": "null is not a number", + "data": null, + "valid": false + } + ] + }, + { + "description": "string type matches strings", + "schema": {"type": "string"}, + "tests": [ + { + "description": "1 is not a string", + "data": 1, + "valid": false + }, + { + "description": "a float is not a string", + "data": 1.1, + "valid": false + }, + { + "description": "a string is a string", + "data": "foo", + "valid": true + }, + { + "description": "a string is still a string, even if it looks like a number", + "data": "1", + "valid": true + }, + { + "description": "an empty string is still a string", + "data": "", + "valid": true + }, + { + "description": "an object is not a string", + "data": {}, + "valid": false + }, + { + "description": "an array is not a string", + "data": [], + "valid": false + }, + { + "description": "a boolean is not a string", + "data": true, + "valid": false + }, + { + "description": "null is not a string", + "data": null, + "valid": false + } + ] + }, + { + "description": "object type matches objects", + "schema": {"type": "object"}, + "tests": [ + { + "description": "an integer is not an object", + "data": 1, + "valid": false + }, + { + "description": "a float is not an object", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an object", + "data": "foo", + "valid": false + }, + { + "description": "an object is an object", + "data": {}, + "valid": true + }, + { + "description": "an array is not an object", + "data": [], + "valid": false + }, + { + "description": "a boolean is not an object", + "data": true, + "valid": false + }, + { + "description": "null is not an object", + "data": null, + "valid": false + } + ] + }, + { + "description": "array type matches arrays", + "schema": {"type": "array"}, + "tests": [ + { + "description": "an integer is not an array", + "data": 1, + "valid": false + }, + { + "description": "a float is not an array", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not an array", + "data": "foo", + "valid": false + }, + { + "description": "an object is not an array", + "data": {}, + "valid": false + }, + { + "description": "an array is an array", + "data": [], + "valid": true + }, + { + "description": "a boolean is not an array", + "data": true, + "valid": false + }, + { + "description": "null is not an array", + "data": null, + "valid": false + } + ] + }, + { + "description": "boolean type matches booleans", + "schema": {"type": "boolean"}, + "tests": [ + { + "description": "an integer is not a boolean", + "data": 1, + "valid": false + }, + { + "description": "zero is not a boolean", + "data": 0, + "valid": false + }, + { + "description": "a float is not a boolean", + "data": 1.1, + "valid": false + }, + { + "description": "a string is not a boolean", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not a boolean", + "data": "", + "valid": false + }, + { + "description": "an object is not a boolean", + "data": {}, + "valid": false + }, + { + "description": "an array is not a boolean", + "data": [], + "valid": false + }, + { + "description": "true is a boolean", + "data": true, + "valid": true + }, + { + "description": "false is a boolean", + "data": false, + "valid": true + }, + { + "description": "null is not a boolean", + "data": null, + "valid": false + } + ] + }, + { + "description": "null type matches only the null object", + "schema": {"type": "null"}, + "tests": [ + { + "description": "an integer is not null", + "data": 1, + "valid": false + }, + { + "description": "a float is not null", + "data": 1.1, + "valid": false + }, + { + "description": "zero is not null", + "data": 0, + "valid": false + }, + { + "description": "a string is not null", + "data": "foo", + "valid": false + }, + { + "description": "an empty string is not null", + "data": "", + "valid": false + }, + { + "description": "an object is not null", + "data": {}, + "valid": false + }, + { + "description": "an array is not null", + "data": [], + "valid": false + }, + { + "description": "true is not null", + "data": true, + "valid": false + }, + { + "description": "false is not null", + "data": false, + "valid": false + }, + { + "description": "null is null", + "data": null, + "valid": true + } + ] + }, + { + "description": "multiple types can be specified in an array", + "schema": {"type": ["integer", "string"]}, + "tests": [ + { + "description": "an integer is valid", + "data": 1, + "valid": true + }, + { + "description": "a string is valid", + "data": "foo", + "valid": true + }, + { + "description": "a float is invalid", + "data": 1.1, + "valid": false + }, + { + "description": "an object is invalid", + "data": {}, + "valid": false + }, + { + "description": "an array is invalid", + "data": [], + "valid": false + }, + { + "description": "a boolean is invalid", + "data": true, + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type as array with one item", + "schema": { + "type": ["string"] + }, + "tests": [ + { + "description": "string is valid", + "data": "foo", + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + } + ] + }, + { + "description": "type: array or object", + "schema": { + "type": ["array", "object"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + }, + { + "description": "null is invalid", + "data": null, + "valid": false + } + ] + }, + { + "description": "type: array, object or null", + "schema": { + "type": ["array", "object", "null"] + }, + "tests": [ + { + "description": "array is valid", + "data": [1,2,3], + "valid": true + }, + { + "description": "object is valid", + "data": {"foo": 123}, + "valid": true + }, + { + "description": "null is valid", + "data": null, + "valid": true + }, + { + "description": "number is invalid", + "data": 123, + "valid": false + }, + { + "description": "string is invalid", + "data": "foo", + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedItems.json b/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedItems.json new file mode 100755 index 00000000..32f13f82 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedItems.json @@ -0,0 +1,437 @@ +[ + { + "description": "unevaluatedItems true", + "schema": { + "type": "array", + "unevaluatedItems": true + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems false", + "schema": { + "type": "array", + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems as schema", + "schema": { + "type": "array", + "unevaluatedItems": { "type": "string" } + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with valid unevaluated items", + "data": ["foo"], + "valid": true + }, + { + "description": "with invalid unevaluated items", + "data": [42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with uniform items", + "schema": { + "type": "array", + "items": { "type": "string" }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "unevaluatedItems doesn't apply", + "data": ["foo", "bar"], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with tuple", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with additionalItems", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "additionalItems": true, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "unevaluatedItems doesn't apply", + "data": ["foo", 42], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with nested tuple", + "schema": { + "type": "array", + "items": [ + { "type": "string" } + ], + "allOf": [ + { + "items": [ + true, + { "type": "number" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", 42], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", 42, true], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with nested additionalItems", + "schema": { + "type": "array", + "allOf": [ + { + "items": [ + { "type": "string" } + ], + "additionalItems": true + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no additional items", + "data": ["foo"], + "valid": true + }, + { + "description": "with additional items", + "data": ["foo", 42, true], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with nested unevaluatedItems", + "schema": { + "type": "array", + "allOf": [ + { + "items": [ + { "type": "string" } + ] + }, + { + "unevaluatedItems": true + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no additional items", + "data": ["foo"], + "valid": true + }, + { + "description": "with additional items", + "data": ["foo", 42, true], + "valid": true + } + ] + }, + { + "description": "unevaluatedItems with anyOf", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "anyOf": [ + { + "items": [ + true, + { "const": "bar" } + ] + }, + { + "items": [ + true, + true, + { "const": "baz" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "when one schema matches and has no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "when one schema matches and has unevaluated items", + "data": ["foo", "bar", 42], + "valid": false + }, + { + "description": "when two schemas match and has no unevaluated items", + "data": ["foo", "bar", "baz"], + "valid": true + }, + { + "description": "when two schemas match and has unevaluated items", + "data": ["foo", "bar", "baz", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with oneOf", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "oneOf": [ + { + "items": [ + true, + { "const": "bar" } + ] + }, + { + "items": [ + true, + { "const": "baz" } + ] + } + ], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with not", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "not": { + "not": { + "items": [ + true, + { "const": "bar" } + ] + } + }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with unevaluated items", + "data": ["foo", "bar"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with if/then/else", + "schema": { + "type": "array", + "items": [ + { "const": "foo" } + ], + "if": { + "items": [ + true, + { "const": "bar" } + ] + }, + "then": { + "items": [ + true, + true, + { "const": "then" } + ] + }, + "else": { + "items": [ + true, + true, + true, + { "const": "else" } + ] + }, + "unevaluatedItems": false + }, + "tests": [ + { + "description": "when if matches and it has no unevaluated items", + "data": ["foo", "bar", "then"], + "valid": true + }, + { + "description": "when if matches and it has unevaluated items", + "data": ["foo", "bar", "then", "else"], + "valid": false + }, + { + "description": "when if doesn't match and it has no unevaluated items", + "data": ["foo", 42, 42, "else"], + "valid": true + }, + { + "description": "when if doesn't match and it has unevaluated items", + "data": ["foo", 42, 42, "else", 42], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with boolean schemas", + "schema": { + "type": "array", + "allOf": [true], + "unevaluatedItems": false + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": [], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems with $ref", + "schema": { + "type": "array", + "$ref": "#/$defs/bar", + "items": [ + { "type": "string" } + ], + "unevaluatedItems": false, + "$defs": { + "bar": { + "items": [ + true, + { "type": "string" } + ] + } + } + }, + "tests": [ + { + "description": "with no unevaluated items", + "data": ["foo", "bar"], + "valid": true + }, + { + "description": "with unevaluated items", + "data": ["foo", "bar", "baz"], + "valid": false + } + ] + }, + { + "description": "unevaluatedItems can't see inside cousins", + "schema": { + "allOf": [ + { + "items": [ true ] + }, + { + "unevaluatedItems": false + } + ] + }, + "tests": [ + { + "description": "always fails", + "data": [ 1 ], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedProperties.json b/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedProperties.json new file mode 100755 index 00000000..b634be5f --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/unevaluatedProperties.json @@ -0,0 +1,813 @@ +[ + { + "description": "unevaluatedProperties true", + "schema": { + "type": "object", + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties schema", + "schema": { + "type": "object", + "unevaluatedProperties": { + "type": "string", + "minLength": 3 + } + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with valid unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with invalid unevaluated properties", + "data": { + "foo": "fo" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties false", + "schema": { + "type": "object", + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": {}, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent properties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent patternProperties", + "schema": { + "type": "object", + "patternProperties": { + "^foo": { "type": "string" } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with adjacent additionalProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "additionalProperties": true, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with nested properties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "properties": { + "bar": { "type": "string" } + } + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with nested patternProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "patternProperties": { + "^bar": { "type": "string" } + } + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with nested additionalProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "additionalProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no additional properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with additional properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with nested unevaluatedProperties", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": { + "type": "string", + "maxLength": 2 + } + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "unevaluatedProperties with anyOf", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "anyOf": [ + { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + }, + { + "properties": { + "baz": { "const": "baz" } + }, + "required": ["baz"] + }, + { + "properties": { + "quux": { "const": "quux" } + }, + "required": ["quux"] + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "when one matches and has no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "when one matches and has unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "not-baz" + }, + "valid": false + }, + { + "description": "when two match and has no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": true + }, + { + "description": "when two match and has unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz", + "quux": "not-quux" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with oneOf", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "oneOf": [ + { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + }, + { + "properties": { + "baz": { "const": "baz" } + }, + "required": ["baz"] + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "quux": "quux" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with not", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "not": { + "not": { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with if/then/else", + "schema": { + "type": "object", + "if": { + "properties": { + "foo": { "const": "then" } + }, + "required": ["foo"] + }, + "then": { + "properties": { + "bar": { "type": "string" } + }, + "required": ["bar"] + }, + "else": { + "properties": { + "baz": { "type": "string" } + }, + "required": ["baz"] + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "when if is true and has no unevaluated properties", + "data": { + "foo": "then", + "bar": "bar" + }, + "valid": true + }, + { + "description": "when if is true and has unevaluated properties", + "data": { + "foo": "then", + "bar": "bar", + "baz": "baz" + }, + "valid": false + }, + { + "description": "when if is false and has no unevaluated properties", + "data": { + "baz": "baz" + }, + "valid": true + }, + { + "description": "when if is false and has unevaluated properties", + "data": { + "foo": "else", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with dependentSchemas", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "dependentSchemas": { + "foo": { + "properties": { + "bar": { "const": "bar" } + }, + "required": ["bar"] + } + }, + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with boolean schemas", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [true], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties with $ref", + "schema": { + "type": "object", + "$ref": "#/$defs/bar", + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false, + "$defs": { + "bar": { + "properties": { + "bar": { "type": "string" } + } + } + } + }, + "tests": [ + { + "description": "with no unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + }, + { + "description": "with unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar", + "baz": "baz" + }, + "valid": false + } + ] + }, + { + "description": "unevaluatedProperties can't see inside cousins", + "schema": { + "allOf": [ + { + "properties": { + "foo": true + } + }, + { + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "always fails", + "data": { + "foo": 1 + }, + "valid": false + } + ] + }, + { + "description": "nested unevaluatedProperties, outer false, inner true, properties outside", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "nested unevaluatedProperties, outer false, inner true, properties inside", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": true + } + ], + "unevaluatedProperties": false + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": true + } + ] + }, + { + "description": "nested unevaluatedProperties, outer true, inner false, properties outside", + "schema": { + "type": "object", + "properties": { + "foo": { "type": "string" } + }, + "allOf": [ + { + "unevaluatedProperties": false + } + ], + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "nested unevaluatedProperties, outer true, inner false, properties inside", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + } + ], + "unevaluatedProperties": true + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "cousin unevaluatedProperties, true and false, true with properties", + "schema": { + "type": "object", + "allOf": [ + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": true + }, + { + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": false + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + }, + { + "description": "cousin unevaluatedProperties, true and false, false with properties", + "schema": { + "type": "object", + "allOf": [ + { + "unevaluatedProperties": true + }, + { + "properties": { + "foo": { "type": "string" } + }, + "unevaluatedProperties": false + } + ] + }, + "tests": [ + { + "description": "with no nested unevaluated properties", + "data": { + "foo": "foo" + }, + "valid": true + }, + { + "description": "with nested unevaluated properties", + "data": { + "foo": "foo", + "bar": "bar" + }, + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tests/latest/uniqueItems.json b/test/JSON-Schema-Test-Suite/tests/latest/uniqueItems.json new file mode 100755 index 00000000..4846c773 --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tests/latest/uniqueItems.json @@ -0,0 +1,384 @@ +[ + { + "description": "uniqueItems validation", + "schema": {"uniqueItems": true}, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is invalid", + "data": [1, 1], + "valid": false + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": false + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is invalid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": false + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is invalid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": false + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is invalid", + "data": [["foo"], ["foo"]], + "valid": false + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "[1] and [true] are unique", + "data": [[1], [true]], + "valid": true + }, + { + "description": "[0] and [false] are unique", + "data": [[0], [false]], + "valid": true + }, + { + "description": "nested [1] and [true] are unique", + "data": [[[1], "foo"], [[true], "foo"]], + "valid": true + }, + { + "description": "nested [0] and [false] are unique", + "data": [[[0], "foo"], [[false], "foo"]], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1, "{}"], + "valid": true + }, + { + "description": "non-unique heterogeneous types are invalid", + "data": [{}, [1], true, null, {}, 1], + "valid": false + }, + { + "description": "different objects are unique", + "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], + "valid": true + }, + { + "description": "objects are non-unique despite key order", + "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], + "valid": false + }, + { + "description": "{\"a\": false} and {\"a\": 0} are unique", + "data": [{"a": false}, {"a": 0}], + "valid": true + }, + { + "description": "{\"a\": true} and {\"a\": 1} are unique", + "data": [{"a": true}, {"a": 1}], + "valid": true + } + ] + }, + { + "description": "uniqueItems with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is not valid", + "data": [false, true, "foo", "foo"], + "valid": false + }, + { + "description": "non-unique array extended from [true, false] is not valid", + "data": [true, false, "foo", "foo"], + "valid": false + } + ] + }, + { + "description": "uniqueItems with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": true, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is not valid", + "data": [false, false], + "valid": false + }, + { + "description": "[true, true] from items array is not valid", + "data": [true, true], + "valid": false + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + }, + { + "description": "uniqueItems=false validation", + "schema": { "uniqueItems": false }, + "tests": [ + { + "description": "unique array of integers is valid", + "data": [1, 2], + "valid": true + }, + { + "description": "non-unique array of integers is valid", + "data": [1, 1], + "valid": true + }, + { + "description": "numbers are unique if mathematically unequal", + "data": [1.0, 1.00, 1], + "valid": true + }, + { + "description": "false is not equal to zero", + "data": [0, false], + "valid": true + }, + { + "description": "true is not equal to one", + "data": [1, true], + "valid": true + }, + { + "description": "unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "baz"}], + "valid": true + }, + { + "description": "non-unique array of objects is valid", + "data": [{"foo": "bar"}, {"foo": "bar"}], + "valid": true + }, + { + "description": "unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : false}}} + ], + "valid": true + }, + { + "description": "non-unique array of nested objects is valid", + "data": [ + {"foo": {"bar" : {"baz" : true}}}, + {"foo": {"bar" : {"baz" : true}}} + ], + "valid": true + }, + { + "description": "unique array of arrays is valid", + "data": [["foo"], ["bar"]], + "valid": true + }, + { + "description": "non-unique array of arrays is valid", + "data": [["foo"], ["foo"]], + "valid": true + }, + { + "description": "1 and true are unique", + "data": [1, true], + "valid": true + }, + { + "description": "0 and false are unique", + "data": [0, false], + "valid": true + }, + { + "description": "unique heterogeneous types are valid", + "data": [{}, [1], true, null, 1], + "valid": true + }, + { + "description": "non-unique heterogeneous types are valid", + "data": [{}, [1], true, null, {}, 1], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "unique array extended from [false, true] is valid", + "data": [false, true, "foo", "bar"], + "valid": true + }, + { + "description": "unique array extended from [true, false] is valid", + "data": [true, false, "foo", "bar"], + "valid": true + }, + { + "description": "non-unique array extended from [false, true] is valid", + "data": [false, true, "foo", "foo"], + "valid": true + }, + { + "description": "non-unique array extended from [true, false] is valid", + "data": [true, false, "foo", "foo"], + "valid": true + } + ] + }, + { + "description": "uniqueItems=false with an array of items and additionalItems=false", + "schema": { + "items": [{"type": "boolean"}, {"type": "boolean"}], + "uniqueItems": false, + "additionalItems": false + }, + "tests": [ + { + "description": "[false, true] from items array is valid", + "data": [false, true], + "valid": true + }, + { + "description": "[true, false] from items array is valid", + "data": [true, false], + "valid": true + }, + { + "description": "[false, false] from items array is valid", + "data": [false, false], + "valid": true + }, + { + "description": "[true, true] from items array is valid", + "data": [true, true], + "valid": true + }, + { + "description": "extra items are invalid even if unique", + "data": [false, true, null], + "valid": false + } + ] + } +] diff --git a/test/JSON-Schema-Test-Suite/tox.ini b/test/JSON-Schema-Test-Suite/tox.ini new file mode 100755 index 00000000..72fd562e --- /dev/null +++ b/test/JSON-Schema-Test-Suite/tox.ini @@ -0,0 +1,9 @@ +[tox] +minversion = 1.6 +envlist = sanity +skipsdist = True + +[testenv:sanity] +# used just for validating the structure of the test case files themselves +deps = jsonschema>=3.2.0 +commands = {envpython} bin/jsonschema_suite check diff --git a/test/additional_remotes/bar.json b/test/additional_remotes/bar.json new file mode 100644 index 00000000..65490a2e --- /dev/null +++ b/test/additional_remotes/bar.json @@ -0,0 +1,3 @@ +{ + "baz": { "$ref": "http://localhost:4321/string.json#" } +} \ No newline at end of file diff --git a/test/additional_remotes/string.json b/test/additional_remotes/string.json new file mode 100644 index 00000000..5d2d1e51 --- /dev/null +++ b/test/additional_remotes/string.json @@ -0,0 +1,3 @@ +{ + "type": "string" +} \ No newline at end of file diff --git a/test/additional_remotes/string_ref.json b/test/additional_remotes/string_ref.json new file mode 100644 index 00000000..d62a0b74 --- /dev/null +++ b/test/additional_remotes/string_ref.json @@ -0,0 +1 @@ +{ "$ref": "http://localhost:4321/string.json" } \ No newline at end of file diff --git a/test/cover.dart b/test/cover.dart deleted file mode 100644 index b8bfca76..00000000 --- a/test/cover.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'dart:io'; -import 'package:logging/logging.dart'; -import 'package:coverage/coverage.dart'; -import 'runner.dart' as runner; -import 'package:path/path.dart'; - - -main() async { - Logger.root.level = Level.OFF; - Logger.root.onRecord.listen((LogRecord rec) { - print('${rec.level.name}: ${rec.time}: ${rec.message}'); - }); - - final results = await runAndCollect('runner.dart'); - - final untested = {}; - results['coverage'].forEach((var entry) { - final source = entry['source']; - if(!source.contains('package:json_schema')) return; - print(source); - final hits = entry['hits']; - final hitCount = hits.length/2; - for(int i=0; i []).add(lineNumber); - } - } - }); - - print(untested); -} diff --git a/test/invalid_schemas/additionalProperties.json b/test/invalid_schemas/draft4/additionalProperties.json similarity index 100% rename from test/invalid_schemas/additionalProperties.json rename to test/invalid_schemas/draft4/additionalProperties.json diff --git a/test/invalid_schemas/allOf.json b/test/invalid_schemas/draft4/allOf.json similarity index 100% rename from test/invalid_schemas/allOf.json rename to test/invalid_schemas/draft4/allOf.json diff --git a/test/invalid_schemas/anyOf.json b/test/invalid_schemas/draft4/anyOf.json similarity index 100% rename from test/invalid_schemas/anyOf.json rename to test/invalid_schemas/draft4/anyOf.json diff --git a/test/invalid_schemas/cycle.json b/test/invalid_schemas/draft4/cycle.json similarity index 100% rename from test/invalid_schemas/cycle.json rename to test/invalid_schemas/draft4/cycle.json diff --git a/test/invalid_schemas/definitions.json b/test/invalid_schemas/draft4/definitions.json similarity index 100% rename from test/invalid_schemas/definitions.json rename to test/invalid_schemas/draft4/definitions.json diff --git a/test/invalid_schemas/dependencies.json b/test/invalid_schemas/draft4/dependencies.json similarity index 100% rename from test/invalid_schemas/dependencies.json rename to test/invalid_schemas/draft4/dependencies.json diff --git a/test/invalid_schemas/description.json b/test/invalid_schemas/draft4/description.json similarity index 100% rename from test/invalid_schemas/description.json rename to test/invalid_schemas/draft4/description.json diff --git a/test/invalid_schemas/enum.json b/test/invalid_schemas/draft4/enum.json similarity index 100% rename from test/invalid_schemas/enum.json rename to test/invalid_schemas/draft4/enum.json diff --git a/test/invalid_schemas/exclusiveMaximum.json b/test/invalid_schemas/draft4/exclusiveMaximum.json similarity index 100% rename from test/invalid_schemas/exclusiveMaximum.json rename to test/invalid_schemas/draft4/exclusiveMaximum.json diff --git a/test/invalid_schemas/exclusiveMinimum.json b/test/invalid_schemas/draft4/exclusiveMinimum.json similarity index 100% rename from test/invalid_schemas/exclusiveMinimum.json rename to test/invalid_schemas/draft4/exclusiveMinimum.json diff --git a/test/invalid_schemas/freeFormProperty.json b/test/invalid_schemas/draft4/freeFormProperty.json similarity index 100% rename from test/invalid_schemas/freeFormProperty.json rename to test/invalid_schemas/draft4/freeFormProperty.json diff --git a/test/invalid_schemas/id.json b/test/invalid_schemas/draft4/id.json similarity index 100% rename from test/invalid_schemas/id.json rename to test/invalid_schemas/draft4/id.json diff --git a/test/invalid_schemas/items.json b/test/invalid_schemas/draft4/items.json similarity index 100% rename from test/invalid_schemas/items.json rename to test/invalid_schemas/draft4/items.json diff --git a/test/invalid_schemas/maxLength.json b/test/invalid_schemas/draft4/maxLength.json similarity index 100% rename from test/invalid_schemas/maxLength.json rename to test/invalid_schemas/draft4/maxLength.json diff --git a/test/invalid_schemas/maxProperties.json b/test/invalid_schemas/draft4/maxProperties.json similarity index 100% rename from test/invalid_schemas/maxProperties.json rename to test/invalid_schemas/draft4/maxProperties.json diff --git a/test/invalid_schemas/maximum.json b/test/invalid_schemas/draft4/maximum.json similarity index 100% rename from test/invalid_schemas/maximum.json rename to test/invalid_schemas/draft4/maximum.json diff --git a/test/invalid_schemas/minItems.json b/test/invalid_schemas/draft4/minItems.json similarity index 100% rename from test/invalid_schemas/minItems.json rename to test/invalid_schemas/draft4/minItems.json diff --git a/test/invalid_schemas/minLength.json b/test/invalid_schemas/draft4/minLength.json similarity index 100% rename from test/invalid_schemas/minLength.json rename to test/invalid_schemas/draft4/minLength.json diff --git a/test/invalid_schemas/minProperties.json b/test/invalid_schemas/draft4/minProperties.json similarity index 100% rename from test/invalid_schemas/minProperties.json rename to test/invalid_schemas/draft4/minProperties.json diff --git a/test/invalid_schemas/minimum.json b/test/invalid_schemas/draft4/minimum.json similarity index 100% rename from test/invalid_schemas/minimum.json rename to test/invalid_schemas/draft4/minimum.json diff --git a/test/invalid_schemas/multipleOf.json b/test/invalid_schemas/draft4/multipleOf.json similarity index 100% rename from test/invalid_schemas/multipleOf.json rename to test/invalid_schemas/draft4/multipleOf.json diff --git a/test/invalid_schemas/not.json b/test/invalid_schemas/draft4/not.json similarity index 100% rename from test/invalid_schemas/not.json rename to test/invalid_schemas/draft4/not.json diff --git a/test/invalid_schemas/oneOf.json b/test/invalid_schemas/draft4/oneOf.json similarity index 100% rename from test/invalid_schemas/oneOf.json rename to test/invalid_schemas/draft4/oneOf.json diff --git a/test/invalid_schemas/pattern.json b/test/invalid_schemas/draft4/pattern.json similarity index 100% rename from test/invalid_schemas/pattern.json rename to test/invalid_schemas/draft4/pattern.json diff --git a/test/invalid_schemas/patternProperties.json b/test/invalid_schemas/draft4/patternProperties.json similarity index 100% rename from test/invalid_schemas/patternProperties.json rename to test/invalid_schemas/draft4/patternProperties.json diff --git a/test/invalid_schemas/properties.json b/test/invalid_schemas/draft4/properties.json similarity index 100% rename from test/invalid_schemas/properties.json rename to test/invalid_schemas/draft4/properties.json diff --git a/test/invalid_schemas/ref.json b/test/invalid_schemas/draft4/ref.json similarity index 100% rename from test/invalid_schemas/ref.json rename to test/invalid_schemas/draft4/ref.json diff --git a/test/invalid_schemas/required.json b/test/invalid_schemas/draft4/required.json similarity index 100% rename from test/invalid_schemas/required.json rename to test/invalid_schemas/draft4/required.json diff --git a/test/invalid_schemas/title.json b/test/invalid_schemas/draft4/title.json similarity index 100% rename from test/invalid_schemas/title.json rename to test/invalid_schemas/draft4/title.json diff --git a/test/invalid_schemas/type.json b/test/invalid_schemas/draft4/type.json similarity index 100% rename from test/invalid_schemas/type.json rename to test/invalid_schemas/draft4/type.json diff --git a/test/invalid_schemas/uniqueItems.json b/test/invalid_schemas/draft4/uniqueItems.json similarity index 100% rename from test/invalid_schemas/uniqueItems.json rename to test/invalid_schemas/draft4/uniqueItems.json diff --git a/test/relative_refs/root.json b/test/relative_refs/root.json new file mode 100644 index 00000000..3cfbab9f --- /dev/null +++ b/test/relative_refs/root.json @@ -0,0 +1,11 @@ +{ + "$id": "root.json", + "$schema": "http://json-schema.org/draft-06/schema#", + "title": "Root Schema", + "description": "A schema with relative refs to other schema files", + "type": "object", + "properties": { + "integer": {"$ref": "subdir/integer.json"}, + "string": {"$ref": "string.json"} + } +} diff --git a/test/relative_refs/string.json b/test/relative_refs/string.json new file mode 100644 index 00000000..5d2d1e51 --- /dev/null +++ b/test/relative_refs/string.json @@ -0,0 +1,3 @@ +{ + "type": "string" +} \ No newline at end of file diff --git a/test/relative_refs/subdir/integer.json b/test/relative_refs/subdir/integer.json new file mode 100755 index 00000000..e38e3be3 --- /dev/null +++ b/test/relative_refs/subdir/integer.json @@ -0,0 +1,4 @@ +{ + "$id": "some_id_that_doesn't_match_ref#", + "type": "integer" +} \ No newline at end of file diff --git a/test/run.sh b/test/run.sh deleted file mode 100755 index ce81fbd9..00000000 --- a/test/run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -dart tool/hop_runner.dart analyze_lib -dart test/runner.dart diff --git a/test/runner.dart b/test/runner.dart deleted file mode 100644 index 5614b9dc..00000000 --- a/test/runner.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:logging/logging.dart'; -import 'test_invalid_schemas.dart' as test_invalid_schemas; -import 'test_validation.dart' as test_validation; - -void main() { - Logger.root.level = Level.OFF; - Logger.root.onRecord.listen((LogRecord rec) { - print('${rec.level.name}: ${rec.time}: ${rec.message}'); - }); - - test_invalid_schemas.main(null); - test_validation.main(null); -} diff --git a/test/test_invalid_schemas.dart b/test/test_invalid_schemas.dart deleted file mode 100644 index b349c70c..00000000 --- a/test/test_invalid_schemas.dart +++ /dev/null @@ -1,65 +0,0 @@ -library json_schema.test_invalid_schemas; - -import 'dart:convert' as convert; -import 'dart:io'; -import 'package:json_schema/json_schema.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; -import 'package:test/test.dart'; - -// custom - -// end - -final Logger _logger = new Logger('test_invalid_schemas'); - -// custom -// end - -void main([List args]) { - if (args?.isEmpty ?? false) { - Logger.root.onRecord.listen( - (LogRecord r) => print("${r.loggerName} [${r.level}]:\t${r.message}")); - Logger.root.level = Level.OFF; - } -// custom
- - String here = - path.dirname(path.dirname(path.absolute(Platform.script.toFilePath()))); - - Directory testSuiteFolder = new Directory("${here}/test/invalid_schemas"); - - testSuiteFolder.listSync().forEach((testEntry) { - String shortName = path.basename(testEntry.path); - group("Invalid schema: ${shortName}", () { - if (testEntry is File) { - List tests = - convert.JSON.decode((testEntry as File).readAsStringSync()); - tests.forEach((testObject) { - var schemaData = testObject["schema"]; - var description = testObject["description"]; - test(description, () { - var gotException = (e) { - _logger.info("Caught expected $e"); - if (!(e is FormatException)) { - _logger.info('${shortName} wtf it is a ${e.runtimeType}'); - } - expect(e is FormatException, true); - }; - var ensureInvalid = expectAsync(gotException); - - try { - Schema.createSchema(schemaData).then(ensureInvalid); - } on FormatException catch (e) { - ensureInvalid(e); - } catch (e) { - ensureInvalid(e); - } - }); - }); - } - }); - }); - -// end
-} diff --git a/test/test_validation.dart b/test/test_validation.dart deleted file mode 100644 index 5865c1ab..00000000 --- a/test/test_validation.dart +++ /dev/null @@ -1,93 +0,0 @@ -library json_schema.test_validation; - -import 'dart:convert' as convert; -import 'dart:io'; -import 'package:json_schema/json_schema.dart'; -import 'package:logging/logging.dart'; -import 'package:path/path.dart' as path; -import 'package:test/test.dart'; - -// custom -// end - -final Logger _logger = new Logger('test_validation'); - -// custom -// end - -void main([List args]) { - if (args?.isEmpty ?? false) { - Logger.root.onRecord.listen( - (LogRecord r) => print("${r.loggerName} [${r.level}]:\t${r.message}")); - Logger.root.level = Level.OFF; - } -// custom
- - //////////////////////////////////////////////////////////////////////// - // Uncomment to see logging of excpetions - // Logger.root.onRecord.listen((LogRecord r) => - // print("${r.loggerName} [${r.level}]:\t${r.message}")); - - Logger.root.level = Level.OFF; - - String here = - path.dirname(path.dirname(path.absolute(Platform.script.toFilePath()))); - - Directory testSuiteFolder = new Directory( - "${here}/test/JSON-Schema-Test-Suite/tests/draft4/invalidSchemas"); - - testSuiteFolder = - new Directory("${here}/test/JSON-Schema-Test-Suite/tests/draft4"); - - var optionals = - new Directory(path.joinAll([testSuiteFolder.path, 'optional'])); - - var all = testSuiteFolder.listSync()..addAll(optionals.listSync()); - - all.forEach((testEntry) { - if (testEntry is File) { - group("Validations ${path.basename(testEntry.path)}", () { - // TODO: add these back or get replacements - // Skip these for now - reason shown - if ([ - 'refRemote.json', // seems to require webserver running to vend files - ].contains(path.basename(testEntry.path))) return; - - List tests = - convert.JSON.decode((testEntry as File).readAsStringSync()); - tests.forEach((testEntry) { - var schemaData = testEntry["schema"]; - var description = testEntry["description"]; - List validationTests = testEntry["tests"]; - - validationTests.forEach((validationTest) { - String validationDescription = validationTest["description"]; - test("${description} : ${validationDescription}", () { - var instance = validationTest["data"]; - bool validationResult; - bool expectedResult = validationTest["valid"]; - var checkResult = - expectAsync(() => expect(validationResult, expectedResult)); - Schema.createSchema(schemaData).then((schema) { - validationResult = schema.validate(instance); - checkResult(); - }); - }); - }); - }); - }); - } - }); - - test("Schema self validation", () { - // Pull in the official schema, verify description and then ensure - // that the schema satisfies the schema for schemas - String url = "http://json-schema.org/draft-04/schema"; - Schema.createSchemaFromUrl(url).then((schema) { - expect(schema.schemaMap["description"], "Core schema meta-schema"); - expect(schema.validate(schema.schemaMap), true); - }); - }); - -// end
-} diff --git a/test/unit/browser/json_schema/validation_test.dart b/test/unit/browser/json_schema/validation_test.dart new file mode 100644 index 00000000..0015855e --- /dev/null +++ b/test/unit/browser/json_schema/validation_test.dart @@ -0,0 +1,74 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@TestOn('browser') + +library json_schema.test_validation; + +import 'package:json_schema/json_schema.dart'; +import 'package:json_schema/browser.dart'; +import 'package:logging/logging.dart'; +import 'package:test/test.dart'; + +final Logger _logger = Logger('test_validation'); + +void main([List args]) { + configureJsonSchemaForBrowser(); + + if (args?.isEmpty == true) { + Logger.root.onRecord.listen((LogRecord r) => print('${r.loggerName} [${r.level}]:\t${r.message}')); + Logger.root.level = Level.OFF; + } + + //////////////////////////////////////////////////////////////////////// + // Uncomment to see logging of exceptions + // Logger.root.onRecord.listen((LogRecord r) => + // print('${r.loggerName} [${r.level}]:\t${r.message}')); + + Logger.root.level = Level.OFF; + + test('Schema self validation', () { + // Pull in the official schema, verify description and then ensure + // that the schema satisfies the schema for schemas + final url = 'http://json-schema.org/draft-04/schema'; + JsonSchema.createSchemaFromUrl(url).then((schema) { + expect(schema.schemaMap['description'], 'Core schema meta-schema'); + expect(schema.validate(schema.schemaMap), true); + }); + }); +} diff --git a/test/unit/vm/json_schema/invalid_schemas_test.dart b/test/unit/vm/json_schema/invalid_schemas_test.dart new file mode 100644 index 00000000..1313a677 --- /dev/null +++ b/test/unit/vm/json_schema/invalid_schemas_test.dart @@ -0,0 +1,94 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@TestOn('vm') + +library json_schema.test_invalid_schemas; + +import 'dart:convert'; +import 'dart:io'; + +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +import 'package:json_schema/json_schema.dart'; +import 'package:json_schema/vm.dart'; + +final Logger _logger = Logger('test_invalid_schemas'); + +void main([List args]) { + configureJsonSchemaForVm(); + + if (args?.isEmpty == true) { + Logger.root.onRecord.listen((LogRecord r) => print('${r.loggerName} [${r.level}]:\t${r.message}')); + Logger.root.level = Level.OFF; + } + + final Directory testSuiteFolder = Directory('./test/invalid_schemas/draft4'); + + testSuiteFolder.listSync().forEach((testEntry) { + final String shortName = path.basename(testEntry.path); + group('Invalid schema (draft4): ${shortName}', () { + if (testEntry is File) { + final List tests = json.decode((testEntry).readAsStringSync()); + tests.forEach((testObject) { + final schemaData = testObject['schema']; + final description = testObject['description']; + + test(description, () async { + final catchException = expectAsync1((e) { + _logger.info('Caught expected $e'); + if (e is! FormatException) { + _logger.info('${shortName} threw an unexpected error type of ${e.runtimeType}'); + } + expect(e is FormatException, true); + }); + + try { + await JsonSchema.createSchemaAsync(schemaData, schemaVersion: SchemaVersion.draft4); + fail('Schema is expected to be invalid, but was not.'); + } catch (e) { + catchException(e); + } + }); + }); + } + }); + }); +} diff --git a/test/unit/vm/json_schema/validation_error_test.dart b/test/unit/vm/json_schema/validation_error_test.dart new file mode 100644 index 00000000..c32944f0 --- /dev/null +++ b/test/unit/vm/json_schema/validation_error_test.dart @@ -0,0 +1,638 @@ +// This test suite verifies that validation errors report correct values for +// instance & schema paths. + +@TestOn('vm') + +import 'package:json_schema/json_schema.dart'; +import 'package:test/test.dart'; + +JsonSchema createObjectSchema(Map nestedSchema) { + return JsonSchema.createSchema({ + 'properties': {'someKey': nestedSchema} + }); +} + +void main() { + group('ValidationError', () { + test('boolean false at root', () { + final schema = JsonSchema.createSchema(false); + final errors = schema.validateWithErrors({'someKey': 1}); + + expect(errors.length, 1); + expect(errors[0].instancePath, ''); + expect(errors[0].schemaPath, ''); + expect(errors[0].message, contains('boolean == false')); + }); + + test('boolean false in object', () { + final schema = JsonSchema.createSchema({ + 'properties': {'someKey': false} + }); + final errors = schema.validateWithErrors({'someKey': 1}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('boolean == false')); + }); + + test('type', () { + final schema = createObjectSchema({"type": "string"}); + final errors = schema.validateWithErrors({'someKey': 1}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('type')); + }); + + test('const', () { + final schema = createObjectSchema({'const': 'foo'}); + final errors = schema.validateWithErrors({'someKey': 'bar'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('const')); + }); + + test('enum', () { + final schema = createObjectSchema({ + 'enum': [1, 2, 3] + }); + final errors = schema.validateWithErrors({'someKey': 4}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('enum')); + }); + + group('string', () { + final schema = createObjectSchema({'minLength': 3, 'maxLength': 5, 'pattern': '^a.*\$'}); + + test('minLength', () { + final errors = schema.validateWithErrors({'someKey': 'ab'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('minLength')); + }); + + test('maxLength', () { + final errors = schema.validateWithErrors({'someKey': 'abcdef'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('maxLength')); + }); + + test('pattern', () { + final errors = schema.validateWithErrors({'someKey': 'bye'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('pattern')); + }); + }); + + group('number', () { + final schema = createObjectSchema({ + 'properties': { + 'nonexclusive': { + 'maximum': 5.0, + 'minimum': 3.0, + }, + 'exclusive': {'exclusiveMaximum': 5.0, 'exclusiveMinimum': 3.0}, + 'multiple': {'multipleOf': 2} + } + }); + + test('maximum', () { + final errors = schema.validateWithErrors({ + 'someKey': {'nonexclusive': 5.1} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/nonexclusive'); + expect(errors[0].schemaPath, '/properties/someKey/properties/nonexclusive'); + expect(errors[0].message, contains('maximum')); + }); + + test('minimum', () { + final errors = schema.validateWithErrors({ + 'someKey': {'nonexclusive': 2.9} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/nonexclusive'); + expect(errors[0].schemaPath, '/properties/someKey/properties/nonexclusive'); + expect(errors[0].message, contains('minimum')); + }); + + test('exclusiveMaximum', () { + final errors = schema.validateWithErrors({ + 'someKey': {'exclusive': 5.0} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/exclusive'); + expect(errors[0].schemaPath, '/properties/someKey/properties/exclusive'); + expect(errors[0].message, contains('exclusiveMaximum')); + }); + + test('exclusiveMinimum', () { + final errors = schema.validateWithErrors({ + 'someKey': {'exclusive': 3.0} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/exclusive'); + expect(errors[0].schemaPath, '/properties/someKey/properties/exclusive'); + expect(errors[0].message, contains('exclusiveMinimum')); + }); + + test('multipleOf', () { + final errors = schema.validateWithErrors({ + 'someKey': {'multiple': 7} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/multiple'); + expect(errors[0].schemaPath, '/properties/someKey/properties/multiple'); + expect(errors[0].message, contains('multipleOf')); + }); + }); + + test('items with the same schema', () { + final schema = createObjectSchema({ + 'items': {'type': 'integer'} + }); + final errors = schema.validateWithErrors({ + 'someKey': [1, 2, 'foo'] + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/2'); + expect(errors[0].schemaPath, '/properties/someKey/items'); + expect(errors[0].message, contains('type')); + }); + + test('items with an array of schemas', () { + final schema = createObjectSchema({ + 'items': [ + {'type': 'integer'}, + {'type': 'string'} + ] + }); + final errors = schema.validateWithErrors({ + 'someKey': ['foo', 'bar'] + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/0'); + expect(errors[0].schemaPath, '/properties/someKey/items/0'); + expect(errors[0].message, contains('type')); + }); + + test('defined items with type checking of additional items', () { + final schema = createObjectSchema({ + 'items': [ + {'type': 'string'}, + {'type': 'string'} + ], + 'additionalItems': {'type': 'integer'} + }); + final errors = schema.validateWithErrors({ + 'someKey': ['foo', 'bar', 'baz'] + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/2'); + expect(errors[0].schemaPath, '/properties/someKey/additionalItems'); + expect(errors[0].message, contains('type')); + }); + + test('defined items with no additional items allowed', () { + final schema = createObjectSchema({ + 'items': [ + {'type': 'string'}, + {'type': 'string'} + ], + 'additionalItems': false + }); + final errors = schema.validateWithErrors({ + 'someKey': ['foo', 'bar', 'baz'] + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/additionalItems'); + expect(errors[0].message, contains('additionalItems')); + }); + + test('simple allOf schema', () { + final schema = createObjectSchema({ + "allOf": [ + {"type": "string"}, + {"maxLength": 5} + ] + }); + + final errors = schema.validateWithErrors({'someKey': 'a long string'}); + + for (var err in errors) print(err); + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/allOf'); + expect(errors[0].message, contains('allOf')); + }); + + test('allOf with an additional base schema', () { + final schema = createObjectSchema({ + "properties": { + "bar": {"type": "integer"} + }, + "required": ["bar"], + "allOf": [ + { + "properties": { + "foo": {"type": "string"} + }, + "required": ["foo"] + }, + { + "properties": { + "baz": {"type": "null"} + }, + "required": ["baz"] + } + ] + }); + + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 'string', 'bar': 1} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/allOf'); + expect(errors[0].message, contains('allOf')); + }); + + test('anyOf', () { + final schema = createObjectSchema({ + "anyOf": [ + {"type": "integer"}, + {"minimum": 2} + ] + }); + + final errors = schema.validateWithErrors({'someKey': 1.5}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/anyOf'); + expect(errors[0].message, contains('type')); + }); + + test('oneOf', () { + final schema = createObjectSchema({ + "oneOf": [ + {"type": "integer"}, + {"minimum": 2} + ] + }); + + final errors = schema.validateWithErrors({'someKey': 3}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/oneOf'); + expect(errors[0].message, contains('oneOf')); + }); + + test('not', () { + final schema = createObjectSchema({ + "not": {"type": "integer"} + }); + + final errors = schema.validateWithErrors({'someKey': 3}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/not'); + expect(errors[0].message, contains('not')); + }); + + test('date-time format', () { + final schema = createObjectSchema({'format': 'date-time'}); + + final errors = schema.validateWithErrors({'someKey': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('date-time')); + }); + + test('URI format', () { + final schema = createObjectSchema({'format': 'uri'}); + + final errors = schema.validateWithErrors({'someKey': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('uri')); + }); + + test('URI reference format', () { + final schema = createObjectSchema({'format': 'uri-reference'}); + + final errors = schema.validateWithErrors({'someKey': '\\\\WINDOWS\\fileshare'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('uri-reference')); + }); + + test('URI template format', () { + final schema = createObjectSchema({'format': 'uri-template'}); + + final errors = schema.validateWithErrors({'someKey': 'http://example.com/dictionary/{term:1}/{term'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('uri-template')); + }); + + test('Email address format', () { + final schema = createObjectSchema({'format': 'email'}); + + final errors = schema.validateWithErrors({'someKey': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('email')); + }); + + test('IPv4 format', () { + final schema = createObjectSchema({'format': 'ipv4'}); + + final errors = schema.validateWithErrors({'someKey': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('ipv4')); + }); + + test('IPv6 format', () { + final schema = createObjectSchema({'format': 'ipv6'}); + + final errors = schema.validateWithErrors({'someKey': '::foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('ipv6')); + }); + + test('Hostname format', () { + final schema = createObjectSchema({'format': 'hostname'}); + + final errors = schema.validateWithErrors({'someKey': 'not_valid'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('hostname')); + }); + + test('JSON Pointer format', () { + final schema = createObjectSchema({'format': 'json-pointer'}); + + final errors = schema.validateWithErrors({'someKey': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('json-pointer')); + }); + + test('Unsupported format', () { + final schema = createObjectSchema({'format': 'fake-format'}); + + final errors = schema.validateWithErrors({'someKey': '3'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('not supported')); + }); + + test('Object minProperties', () { + final schema = createObjectSchema({'minProperties': 2}); + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 'bar'} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('minProperties')); + }); + + test('Object maxProperties', () { + final schema = createObjectSchema({'maxProperties': 1}); + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 1, 'bar': 2} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey'); + expect(errors[0].message, contains('maxProperties')); + }); + + test('Object required properties', () { + final schema = createObjectSchema({ + 'properties': { + 'foo': {'type': 'string'}, + 'bar': {'type': 'string'} + }, + 'required': ['foo', 'bar'] + }); + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 'a'} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/required'); + expect(errors[0].message, contains('required')); + }); + + test('Object pattern properties', () { + final schema = createObjectSchema({ + 'patternProperties': { + 'f.*o': {'type': 'integer'}, + } + }); + final errors = schema.validateWithErrors({ + 'someKey': {'foooooooo': 'a'} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/foooooooo'); + expect(errors[0].schemaPath, '/properties/someKey/patternProperties/f.*o'); + expect(errors[0].message, contains('type')); + }); + + test('Object additional properties not allowed', () { + final schema = createObjectSchema({ + 'properties': { + 'foo': {'type': 'string'}, + 'bar': {'type': 'string'} + }, + 'additionalProperties': false + }); + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 'a', 'bar': 'b', 'baz': 'c'} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/additionalProperties'); + expect(errors[0].message, contains('additional property')); + }); + + test('Object additional properties with schema', () { + final schema = createObjectSchema({ + 'properties': { + 'foo': {'type': 'string'}, + 'bar': {'type': 'string'} + }, + 'additionalProperties': {'type': 'string'} + }); + final errors = schema.validateWithErrors({ + 'someKey': {'foo': 'a', 'bar': 'b', 'baz': 3} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey/baz'); + expect(errors[0].schemaPath, '/properties/someKey/additionalProperties'); + expect(errors[0].message, contains('type')); + }); + + test('Object with property dependencies', () { + final schema = createObjectSchema({ + 'dependencies': { + 'bar': ['foo'] + } + }); + final errors = schema.validateWithErrors({ + 'someKey': {'bar': 'b'} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/dependencies'); + expect(errors[0].message, contains('required')); + }); + + test('Object with schema dependencies', () { + final schema = createObjectSchema({ + "dependencies": { + "bar": { + "properties": { + "foo": {"type": "integer"}, + "bar": {"type": "integer"} + } + } + } + }); + final errors = schema.validateWithErrors({ + 'someKey': {"foo": 2, "bar": "quux"} + }); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/someKey'); + expect(errors[0].schemaPath, '/properties/someKey/dependencies/bar'); + expect(errors[0].message, contains('schema dependency')); + }); + + group('string formatting', () { + final schema = JsonSchema.createSchema({ + "properties": { + "foo": {"type": "string"}, + "bar": {"type": "integer"} + }, + "required": ["foo"] + }); + + test('with an instance path should include the path', () { + final errors = schema.validateWithErrors({'foo': 'some string', 'bar': 'oops this should be an integer'}); + expect(errors.length, 1); + expect(errors[0].toString().startsWith('/bar:'), isTrue); + }); + + test('without an instance path should add "root" instead of the path', () { + final errors = schema.validateWithErrors({}); + expect(errors.length, 1); + expect(errors[0].toString(), '# (root): required prop missing: foo from {}'); + }); + }); + + group('reference', () { + final schemaJson = { + 'properties': { + 'minItems': {'minItems': 2}, + 'maxItems': {'maxItems': 2}, + 'minLength': {'\$ref': '#/properties/refDestination'}, + 'refDestination': {'minLength': 5}, + 'maxLength': {'\$ref': 'http://localhost/destination.json'}, + 'stringArray': { + 'type': 'array', + 'items': {'type': 'string'} + }, + } + }; + + final RefProvider syncRefProvider = RefProvider.syncSchema((String ref) { + final refs = { + 'http://localhost/destination.json': {'maxLength': 2} + }; + + return JsonSchema.createSchema(refs[ref]); + }); + + final schema = JsonSchema.createSchema(schemaJson, refProvider: syncRefProvider); + + test('local', () { + final errors = schema.validateWithErrors({'minLength': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/minLength'); + expect(errors[0].schemaPath, '/properties/refDestination'); + expect(errors[0].message, contains('minLength')); + }); + + test('remote', () { + final errors = schema.validateWithErrors({'maxLength': 'foo'}); + + expect(errors.length, 1); + expect(errors[0].instancePath, '/maxLength'); + expect(errors[0].schemaPath, 'http://localhost/destination.json/'); + expect(errors[0].message, contains('maxLength')); + }); + }); + }); +} diff --git a/test/unit/vm/json_schema/validation_test.dart b/test/unit/vm/json_schema/validation_test.dart new file mode 100644 index 00000000..eb857ee4 --- /dev/null +++ b/test/unit/vm/json_schema/validation_test.dart @@ -0,0 +1,535 @@ +// Copyright 2013-2018 Workiva Inc. +// +// Licensed under the Boost Software License (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.boost.org/LICENSE_1_0.txt +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This software or document includes material copied from or derived +// from JSON-Schema-Test-Suite (https://github.com/json-schema-org/JSON-Schema-Test-Suite), +// Copyright (c) 2012 Julian Berman, which is licensed under the following terms: +// +// Copyright (c) 2012 Julian Berman +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +@TestOn('vm') + +library json_schema.test_validation; + +import 'dart:convert'; +import 'dart:io'; +import 'package:json_schema/json_schema.dart'; +import 'package:json_schema/vm.dart'; +import 'package:json_schema/src/json_schema/constants.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as path; +import 'package:shelf/shelf_io.dart' as io; +import 'package:shelf_static/shelf_static.dart'; +import 'package:test/test.dart'; + +final Logger _logger = Logger('test_validation'); + +void main([List args]) { + configureJsonSchemaForVm(); + + // Serve remotes for ref tests. + final specFileHandler = createStaticHandler('test/JSON-Schema-Test-Suite/remotes'); + io.serve(specFileHandler, 'localhost', 1234); + + final additionalRemotesHandler = createStaticHandler('test/additional_remotes'); + io.serve(additionalRemotesHandler, 'localhost', 4321); + + if (args?.isEmpty == true) { + Logger.root.onRecord.listen((LogRecord r) => print('${r.loggerName} [${r.level}]:\t${r.message}')); + Logger.root.level = Level.OFF; + } + + //////////////////////////////////////////////////////////////////////// + // Uncomment to see logging of exceptions + // Logger.root.onRecord.listen((LogRecord r) => + // print('${r.loggerName} [${r.level}]:\t${r.message}')); + + Logger.root.level = Level.OFF; + + // Draft 4 Tests + final Directory testSuiteFolderV4 = Directory('./test/JSON-Schema-Test-Suite/tests/draft4'); + final Directory optionalsV4 = Directory(path.joinAll([testSuiteFolderV4.path, 'optional'])); + final allDraft4 = testSuiteFolderV4.listSync()..addAll(optionalsV4.listSync()); + + // Draft 6 Tests + final Directory testSuiteFolderV6 = Directory('./test/JSON-Schema-Test-Suite/tests/draft6'); + final Directory optionalsV6 = Directory(path.joinAll([testSuiteFolderV6.path, 'optional'])); + final allDraft6 = testSuiteFolderV6.listSync()..addAll(optionalsV6.listSync()); + + final runAllTestsForDraftX = + (SchemaVersion schemaVersion, List allTests, List skipFiles, List skipTests, + {bool isSync = false, RefProvider refProvider}) { + String shortSchemaVersion = schemaVersion.toString(); + if (schemaVersion == SchemaVersion.draft4) { + shortSchemaVersion = 'draft4'; + } else if (schemaVersion == SchemaVersion.draft6) { + shortSchemaVersion = 'draft6'; + } + + allTests.forEach((testEntry) { + if (testEntry is File) { + group('Validations ($shortSchemaVersion) ${path.basename(testEntry.path)}', () { + // Skip these for now - reason shown. + if (skipFiles.contains(path.basename(testEntry.path))) return; + + final List tests = json.decode((testEntry).readAsStringSync()); + tests.forEach((testEntry) { + final schemaData = testEntry['schema']; + final description = testEntry['description']; + final List validationTests = testEntry['tests']; + + validationTests.forEach((validationTest) { + final String validationDescription = validationTest['description']; + final String testName = '${description} : ${validationDescription}'; + + // Individual test cases to skip - reason listed in comments. + if (skipTests.contains(testName)) return; + + test(testName, () { + final instance = validationTest['data']; + bool validationResult; + final bool expectedResult = validationTest['valid']; + + if (isSync) { + final schema = JsonSchema.createSchema( + schemaData, + schemaVersion: schemaVersion, + refProvider: refProvider, + ); + validationResult = schema.validate(instance); + expect(validationResult, expectedResult); + } else { + final checkResult = expectAsync0(() => expect(validationResult, expectedResult)); + JsonSchema.createSchemaAsync(schemaData, schemaVersion: schemaVersion, refProvider: refProvider) + .then((schema) { + validationResult = schema.validate(instance); + checkResult(); + }); + } + }); + }); + }); + }); + } + }); + }; + + // Mock Ref Provider for refRemote tests. Emulates what createSchemaFromUrl would return. + final RefProvider syncRefJsonProvider = RefProvider.syncJson((String ref) { + switch (ref) { + case 'http://localhost:1234/integer.json': + return json.decode(r''' + { + "type": "integer" + } + '''); + break; + case 'http://localhost:1234/subSchemas.json': + return json.decode(r''' + { + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/integer" + } + } + '''); + break; + case 'http://localhost:1234/subSchemas.json': + return json.decode(r''' + { + "integer": { + "type": "integer" + }, + "refToInteger": { + "$ref": "#/integer" + } + } + '''); + break; + case 'http://localhost:1234/folder/folderInteger.json': + return json.decode(r''' + { + "type": "integer" + } + '''); + break; + case 'http://localhost:1234/name.json': + return json.decode(r''' + { + "definitions": { + "orNull": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#" + } + ] + } + }, + "type": "string" + } + '''); + break; + case 'http://localhost:1234/baseUriChangeFolderInSubschema/folderInteger.json': + return json.decode(''' + { + "type": "integer" + } + '''); + case 'http://localhost:1234/baseUriChangeFolder/folderInteger.json': + return json.decode(''' + { + "type": "integer" + } + '''); + case 'http://localhost:1234/baseUriChange/folderInteger.json': + return json.decode(''' + { + "type": "integer" + } + '''); + default: + return null; + break; + } + }); + + final RefProvider syncRefProvider = RefProvider.syncSchema((String ref) { + final schemaDef = syncRefJsonProvider.provide(ref); + if (schemaDef != null) { + return JsonSchema.createSchema(schemaDef); + } + + return null; + }); + + final RefProvider asyncRefJsonProvider = RefProvider.asyncJson((String ref) async { + // Mock a delayed response. + await Duration(milliseconds: 1); + return syncRefJsonProvider.provide(ref); + }); + + final RefProvider asyncRefProvider = RefProvider.asyncSchema((String ref) async { + // Mock a delayed response. + await Duration(milliseconds: 1); + return syncRefProvider.provide(ref); + }); + + final List commonSkippedFiles = const []; + + /// A list of tests to skip for all drafts. + /// Should match the portion of the test name printed after the JSON file name on test run. + final List commonSkippedTests = const [ + // Parsing of large numbers in JSON changed in Dart 2.2, resulting in + // a more accurate representation of the input, but a type of `double`. + // Previous versions of Dart pass this test, but the integer output is not + // equivalent to the JSON input. + 'integer : a bignum is an integer', + 'integer : a negative bignum is an integer', + // Skip new tests from the spec that we don't pass yet: + 'all integers are multiples of 0.5, if overflow is handled : valid if optional overflow handling is implemented', + ]; + + // Run all tests asynchronously with no ref provider. + runAllTestsForDraftX( + SchemaVersion.draft4, + allDraft4, + commonSkippedFiles, + commonSkippedTests, + ); + runAllTestsForDraftX( + SchemaVersion.draft6, + allDraft6, + commonSkippedFiles, + commonSkippedTests, + ); + + // Run all tests synchronously with a sync ref provider. + runAllTestsForDraftX( + SchemaVersion.draft4, + allDraft4, + commonSkippedFiles, + commonSkippedTests, + isSync: true, + refProvider: syncRefProvider, + ); + runAllTestsForDraftX( + SchemaVersion.draft6, + allDraft6, + commonSkippedFiles, + commonSkippedTests, + isSync: true, + refProvider: syncRefProvider, + ); + + // Run all tests synchronously with a sync json provider. + runAllTestsForDraftX( + SchemaVersion.draft4, + allDraft4, + commonSkippedFiles, + commonSkippedTests, + isSync: true, + refProvider: syncRefJsonProvider, + ); + runAllTestsForDraftX( + SchemaVersion.draft6, + allDraft6, + commonSkippedFiles, + commonSkippedTests, + isSync: true, + refProvider: syncRefJsonProvider, + ); + + // Run all tests asynchronously with an async ref provider. + runAllTestsForDraftX( + SchemaVersion.draft4, + allDraft4, + commonSkippedFiles, + commonSkippedTests, + refProvider: asyncRefProvider, + ); + runAllTestsForDraftX( + SchemaVersion.draft6, + allDraft6, + commonSkippedFiles, + commonSkippedTests, + refProvider: asyncRefProvider, + ); + + // Run all tests asynchronously with an async json provider. + runAllTestsForDraftX( + SchemaVersion.draft4, + allDraft4, + commonSkippedFiles, + commonSkippedTests, + refProvider: asyncRefJsonProvider, + ); + runAllTestsForDraftX( + SchemaVersion.draft6, + allDraft6, + commonSkippedFiles, + commonSkippedTests, + refProvider: asyncRefJsonProvider, + ); + + group('Schema self validation', () { + for (final version in SchemaVersion.values.map((value) => value.toString())) { + test('version: $version', () { + // Pull in the official schema, verify description and then ensure + // that the schema satisfies the schema for schemas. + final url = version; + JsonSchema.createSchemaFromUrl(url).then(expectAsync1((schema) { + expect(schema.validate(schema.schemaMap), isTrue); + })); + }); + } + }); + + group('Nested \$refs in root schema', () { + test('properties', () async { + final barSchema = await JsonSchema.createSchemaAsync({ + "properties": { + "foo": {"\$ref": "http://localhost:1234/integer.json#"}, + "bar": {"\$ref": "http://localhost:4321/string.json#"} + }, + "required": ["foo", "bar"] + }); + + final isValid = barSchema.validate({"foo": 2, "bar": "test"}); + + final isInvalid = barSchema.validate({"foo": 2, "bar": 4}); + + expect(isValid, isTrue); + expect(isInvalid, isFalse); + }); + + test('items', () async { + final schema = await JsonSchema.createSchemaAsync({ + "items": {"\$ref": "http://localhost:1234/integer.json"} + }); + + final isValid = schema.validate([1, 2, 3, 4]); + final isInvalid = schema.validate([1, 2, 3, '4']); + + expect(isValid, isTrue); + expect(isInvalid, isFalse); + }); + + test('not / anyOf', () async { + final schema = await JsonSchema.createSchemaAsync({ + "items": { + "not": { + "anyOf": [ + {"\$ref": "http://localhost:1234/integer.json#"}, + {"\$ref": "http://localhost:4321/string.json#"}, + ] + } + } + }); + + final isValid = schema.validate([3.4]); + final isInvalid = schema.validate(['test']); + + expect(isValid, isTrue); + expect(isInvalid, isFalse); + }); + }); + + group('examples property', () { + group('in draft4', () { + test('should NOT be supported', () { + final schema = JsonSchema.createSchema({ + "type": "string", + "examples": ["This", "message", "is", "lost."] + }, schemaVersion: SchemaVersion.draft4); + + expect(schema.examples.isEmpty, isTrue); + }); + test('should still pass the default value to the examples getter', () { + final schema = JsonSchema.createSchema({ + "type": "string", + "examples": ["This", "message", "is", "lost."], + "default": "But this one isn't.", + }, schemaVersion: SchemaVersion.draft4); + + expect(schema.examples.length, equals(1)); + expect(schema.examples.single, equals("But this one isn't.")); + }); + }); + + group('in draft 6', () { + test('should be supported', () { + final schema = JsonSchema.createSchema({ + "type": "string", + "examples": ["This", "message", "is", "not", "lost!"] + }, schemaVersion: SchemaVersion.draft6); + + expect(schema.examples.length, equals(5)); + expect(schema.examples[4], equals('lost!')); + }); + test('should append the default value to the examples getter', () { + final schema = JsonSchema.createSchema({ + "type": "string", + "examples": ["This", "message", "is", "not", "lost!"], + "default": "And neither is this one", + }, schemaVersion: SchemaVersion.draft6); + + expect(schema.examples.length, equals(6)); + expect(schema.examples[0], equals("This")); + expect(schema.examples[5], equals("And neither is this one")); + }); + }); + }); + + test('Schema from relative filesystem URI should be supported', () async { + // this assumes that tests are run from the root directory of the project + final schema = await JsonSchema.createSchemaFromUrl('test/relative_refs/root.json'); + + expect(schema.validate({"string": 123, "integer": 123}), isFalse); + expect(schema.validate({"string": "a string", "integer": "a string"}), isFalse); + expect(schema.validate({"string": "a string", "integer": 123}), isTrue); + }); + + test('Recursive refs from a remote schema should be supported with a json provider', () async { + final RefProvider syncRefJsonProvider = RefProvider.syncJson((String ref) { + switch (ref) { + case 'http://localhost:1234/tree.json': + return { + "\$id": "http://localhost:1234/tree.json", + "description": "tree of nodes", + "type": "object", + "properties": { + "meta": {"type": "string"}, + "nodes": { + "type": "array", + "items": {"\$ref": "node.json"} + } + }, + "required": ["meta", "nodes"] + }; + case 'http://localhost:1234/node.json': + return { + "\$id": "http://localhost:1234/node.json", + "description": "nodes", + "type": "object", + "properties": { + "value": {"type": "number"}, + "subtree": {"\$ref": "tree.json"} + }, + "required": ["value"] + }; + default: + return null; + } + }); + + final schema = await JsonSchema.createSchema( + syncRefJsonProvider.provide('http://localhost:1234/tree.json'), + refProvider: syncRefJsonProvider, + ); + + final isValid = schema.validate({ + "meta": "a string", + "nodes": [ + { + "value": 123, + "subtree": {"meta": "a string", "nodes": []} + } + ] + }); + + final isInvalid = schema.validate({ + "meta": "a string", + "nodes": [ + { + "value": 123, + "subtree": { + "meta": "a string", + "nodes": [ + { + "value": 123, + "subtree": {"meta": 123, "nodes": []} + } + ] + } + } + ] + }); + + expect(isValid, isTrue); + expect(isInvalid, isFalse); + }); +} diff --git a/tool/dart_dev/config.dart b/tool/dart_dev/config.dart new file mode 100644 index 00000000..484610a7 --- /dev/null +++ b/tool/dart_dev/config.dart @@ -0,0 +1,16 @@ +import 'package:dart_dev/dart_dev.dart'; +import 'package:glob/glob.dart'; + +final config = { + 'analyze': AnalyzeTool() + ..analyzerArgs = ['--fatal-warnings'] + ..include = [ + Glob('bin/'), + Glob('lib/'), + Glob('test/'), + Glob('tool/'), + ], + 'format': FormatTool()..formatterArgs = ['--line-length=120'], + 'serve': WebdevServeTool()..webdevArgs = ['web:9000'], + 'test': TestTool(), +}; diff --git a/tool/travis.sh b/tool/travis.sh deleted file mode 100755 index adc8fc76..00000000 --- a/tool/travis.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -# Fast fail the script on failures. -set -e - -# Skipping this until at least we have a dev release that aligns with dart_style version -# $(dirname -- "$0")/ensure_dartfmt.sh - -# Run the tests. -dart test/runner.dart - -# Run the build.dart file - just to make sure it works -$(dirname $(readlink -f `which dart`))/dartanalyzer lib/*.dart test/*.dart -
Definitions
Definitions
$s
$first$second