git.iko.soy/typeable/hjsonschema
on master help sign in
160 files changed+12957−1185
since splitsnapshots
unifiedsplit
.gitmodules−3 .travis.yml+90−19 JSON-Schema-Test-Suite−1 JSON-Schema-Test-Suite/.gitignore+1 JSON-Schema-Test-Suite/.travis.yml+9 JSON-Schema-Test-Suite/LICENSE+19 JSON-Schema-Test-Suite/README.md+169 JSON-Schema-Test-Suite/bin/jsonschema_suite+258 JSON-Schema-Test-Suite/index.js+32 JSON-Schema-Test-Suite/package.json+28 JSON-Schema-Test-Suite/remotes/folder/folderInteger.json+3 JSON-Schema-Test-Suite/remotes/integer.json+3 JSON-Schema-Test-Suite/remotes/name.json+11 JSON-Schema-Test-Suite/remotes/subSchemas.json+8 JSON-Schema-Test-Suite/shell.nix+8 JSON-Schema-Test-Suite/test-schema.json+28 JSON-Schema-Test-Suite/tests/draft3/additionalItems.json+82 JSON-Schema-Test-Suite/tests/draft3/additionalProperties.json+88 JSON-Schema-Test-Suite/tests/draft3/default.json+49 JSON-Schema-Test-Suite/tests/draft3/dependencies.json+108 JSON-Schema-Test-Suite/tests/draft3/disallow.json+80 JSON-Schema-Test-Suite/tests/draft3/divisibleBy.json+60 JSON-Schema-Test-Suite/tests/draft3/enum.json+71 JSON-Schema-Test-Suite/tests/draft3/extends.json+94 JSON-Schema-Test-Suite/tests/draft3/items.json+46 JSON-Schema-Test-Suite/tests/draft3/maxItems.json+28 JSON-Schema-Test-Suite/tests/draft3/maxLength.json+33 JSON-Schema-Test-Suite/tests/draft3/maximum.json+42 JSON-Schema-Test-Suite/tests/draft3/minItems.json+28 JSON-Schema-Test-Suite/tests/draft3/minLength.json+33 JSON-Schema-Test-Suite/tests/draft3/minimum.json+42 JSON-Schema-Test-Suite/tests/draft3/optional/bignum.json+107 JSON-Schema-Test-Suite/tests/draft3/optional/format.json+222 JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.json+18 JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.json+15 JSON-Schema-Test-Suite/tests/draft3/pattern.json+34 JSON-Schema-Test-Suite/tests/draft3/patternProperties.json+110 JSON-Schema-Test-Suite/tests/draft3/properties.json+92 JSON-Schema-Test-Suite/tests/draft3/ref.json+192 JSON-Schema-Test-Suite/tests/draft3/refRemote.json+74 JSON-Schema-Test-Suite/tests/draft3/required.json+53 JSON-Schema-Test-Suite/tests/draft3/type.json+489 JSON-Schema-Test-Suite/tests/draft3/uniqueItems.json+79 JSON-Schema-Test-Suite/tests/draft4/additionalItems.json+87 JSON-Schema-Test-Suite/tests/draft4/additionalProperties.json+88 JSON-Schema-Test-Suite/tests/draft4/allOf.json+112 JSON-Schema-Test-Suite/tests/draft4/anyOf.json+68 JSON-Schema-Test-Suite/tests/draft4/default.json+49 JSON-Schema-Test-Suite/tests/draft4/definitions.json+32 JSON-Schema-Test-Suite/tests/draft4/dependencies.json+113 JSON-Schema-Test-Suite/tests/draft4/enum.json+72 JSON-Schema-Test-Suite/tests/draft4/items.json+78 JSON-Schema-Test-Suite/tests/draft4/maxItems.json+28 JSON-Schema-Test-Suite/tests/draft4/maxLength.json+33 JSON-Schema-Test-Suite/tests/draft4/maxProperties.json+28 JSON-Schema-Test-Suite/tests/draft4/maximum.json+47 JSON-Schema-Test-Suite/tests/draft4/minItems.json+28 JSON-Schema-Test-Suite/tests/draft4/minLength.json+33 JSON-Schema-Test-Suite/tests/draft4/minProperties.json+28 JSON-Schema-Test-Suite/tests/draft4/minimum.json+47 JSON-Schema-Test-Suite/tests/draft4/multipleOf.json+60 JSON-Schema-Test-Suite/tests/draft4/not.json+96 JSON-Schema-Test-Suite/tests/draft4/oneOf.json+68 JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json+107 JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.json+13 JSON-Schema-Test-Suite/tests/draft4/optional/format.json+148 JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.json+15 JSON-Schema-Test-Suite/tests/draft4/pattern.json+34 JSON-Schema-Test-Suite/tests/draft4/patternProperties.json+110 JSON-Schema-Test-Suite/tests/draft4/properties.json+92 JSON-Schema-Test-Suite/tests/draft4/ref.json+298 JSON-Schema-Test-Suite/tests/draft4/refRemote.json+171 JSON-Schema-Test-Suite/tests/draft4/required.json+44 JSON-Schema-Test-Suite/tests/draft4/type.json+345 JSON-Schema-Test-Suite/tests/draft4/uniqueItems.json+79 JSON-Schema-Test-Suite/tests/draft6/additionalItems.json+87 JSON-Schema-Test-Suite/tests/draft6/additionalProperties.json+88 JSON-Schema-Test-Suite/tests/draft6/allOf.json+145 JSON-Schema-Test-Suite/tests/draft6/anyOf.json+101 JSON-Schema-Test-Suite/tests/draft6/boolean_schema.json+104 JSON-Schema-Test-Suite/tests/draft6/const.json+65 JSON-Schema-Test-Suite/tests/draft6/contains.json+95 JSON-Schema-Test-Suite/tests/draft6/default.json+49 JSON-Schema-Test-Suite/tests/draft6/definitions.json+32 JSON-Schema-Test-Suite/tests/draft6/dependencies.json+162 JSON-Schema-Test-Suite/tests/draft6/enum.json+72 JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.json+30 JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.json+30 JSON-Schema-Test-Suite/tests/draft6/items.json+133 JSON-Schema-Test-Suite/tests/draft6/maxItems.json+28 JSON-Schema-Test-Suite/tests/draft6/maxLength.json+33 JSON-Schema-Test-Suite/tests/draft6/maxProperties.json+28 JSON-Schema-Test-Suite/tests/draft6/maximum.json+28 JSON-Schema-Test-Suite/tests/draft6/minItems.json+28 JSON-Schema-Test-Suite/tests/draft6/minLength.json+33 JSON-Schema-Test-Suite/tests/draft6/minProperties.json+28 JSON-Schema-Test-Suite/tests/draft6/minimum.json+28 JSON-Schema-Test-Suite/tests/draft6/multipleOf.json+60 JSON-Schema-Test-Suite/tests/draft6/not.json+117 JSON-Schema-Test-Suite/tests/draft6/oneOf.json+112 JSON-Schema-Test-Suite/tests/draft6/optional/bignum.json+105 JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.json+13 JSON-Schema-Test-Suite/tests/draft6/optional/format.json+148 JSON-Schema-Test-Suite/tests/draft6/optional/zeroTerminatedFloats.json+15 JSON-Schema-Test-Suite/tests/draft6/pattern.json+34 JSON-Schema-Test-Suite/tests/draft6/patternProperties.json+141 JSON-Schema-Test-Suite/tests/draft6/properties.json+123 JSON-Schema-Test-Suite/tests/draft6/propertyNames.json+68 JSON-Schema-Test-Suite/tests/draft6/ref.json+332 JSON-Schema-Test-Suite/tests/draft6/refRemote.json+171 JSON-Schema-Test-Suite/tests/draft6/required.json+60 JSON-Schema-Test-Suite/tests/draft6/type.json+345 JSON-Schema-Test-Suite/tests/draft6/uniqueItems.json+79 JSON-Schema-Test-Suite/tox.ini+8 README.md+13−49 changelog.md+209 changelog.txt−19 examples/AlternateSchema.hs+200 examples/Simple.hs+37 examples/TwoStep.hs+48 examples/json/start.json+3 examples/json/unique.json+3 hjsonschema.cabal+155−84 shell.nix+15 src/Data/JsonSchema.hs−127 src/Data/JsonSchema/Core.hs−55 src/Data/JsonSchema/Helpers.hs−176 src/Data/JsonSchema/Reference.hs−68 src/Data/JsonSchema/Validators.hs−438 src/Import.hs+19 src/JSONSchema/Draft4.hs+186 src/JSONSchema/Draft4/Failure.hs+58 src/JSONSchema/Draft4/Schema.hs+341 src/JSONSchema/Draft4/Spec.hs+150 src/JSONSchema/Fetch.hs+175 src/JSONSchema/Types.hs+35 src/JSONSchema/Validator/Draft4.hs+242 src/JSONSchema/Validator/Draft4/Any.hs+509 src/JSONSchema/Validator/Draft4/Array.hs+225 src/JSONSchema/Validator/Draft4/Number.hs+83 src/JSONSchema/Validator/Draft4/Object.hs+182 src/JSONSchema/Validator/Draft4/Object/Properties.hs+209 src/JSONSchema/Validator/Draft4/String.hs+78 src/JSONSchema/Validator/Reference.hs+71 src/JSONSchema/Validator/Types.hs+22 src/JSONSchema/Validator/Utils.hs+119 draft4.json → src/draft4.json stack.yaml+2 test/Local.hs+98 test/Local/Failure.hs+105 test/Local/Reference.hs+36 test/Local/Validation.hs+70 test/Local/schema-with-ref.json+3 test/Local/schema.json+3 test/Remote.hs+57 test/Shared.hs+134 test/supplement/recurse.json+17 tests/Lib.hs−105 tests/Local.hs−18 tests/Remote.hs−23
.gitmodulesdeleted−3
@@ -1,3 +0,0 @@
1−[submodule "JSON-Schema-Test-Suite"]
2− path = JSON-Schema-Test-Suite
3− url = https://github.com/json-schema/JSON-Schema-Test-Suite
.travis.yml+90−19
@@ -1,24 +1,95 @@
1−language: haskell
1+# This Travis job script has been generated by a script via
2+#
3+# make_travis_yml_2.hs 'hjsonschema.cabal'
4+#
5+# For more information, see https://github.com/hvr/multi-ghc-travis
6+#
7+language: c
8+sudo: false
29
3−ghc:
4− - 7.6
5− - 7.8
10+git:
11+ submodules: false # whether to recursively clone submodules
12+
13+cache:
14+ directories:
15+ - $HOME/.cabal/packages
16+ - $HOME/.cabal/store
17+
18+before_cache:
19+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
20+ # remove files that are regenerated by 'cabal update'
21+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
22+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
23+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
24+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
25+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
26+
27+matrix:
28+ include:
29+ - compiler: "ghc-8.0.2"
30+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
31+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
32+ - compiler: "ghc-8.2.2"
33+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
34+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
35+ - compiler: "ghc-8.4.1"
36+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
37+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.4.1], sources: [hvr-ghc]}}
638
739before_install:
8− - cabal install alex happy
40+ - HC=${CC}
41+ - HCPKG=${HC/ghc/ghc-pkg}
42+ - unset CC
43+ - PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH
44+ - PKGNAME='hjsonschema'
45+
46+install:
47+ - cabal --version
48+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
49+ - BENCH=${BENCH---enable-benchmarks}
50+ - TEST=${TEST---enable-tests}
51+ - HADDOCK=${HADDOCK-true}
52+ - INSTALLED=${INSTALLED-true}
53+ - travis_retry cabal update -v
54+ - sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
55+ - rm -fv cabal.project.local
56+ - "echo 'packages: .' > cabal.project"
57+ - rm -f cabal.project.freeze
58+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --dep -j2 all
59+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --dep -j2 all
960
61+# Here starts the actual work to be performed for the package under test;
62+# any command which exits with a non-zero exit code causes the build to fail.
1063script:
11− - cabal configure --enable-tests
12− - cabal test
13− - cabal check
14− - cabal sdist
15−
16− # check that the generated source-distribution can be built & installed
17− - export SRC_TGZ=$(cabal info . | awk '{print $2 ".tar.gz";exit}') ;
18− cd dist/;
19− if [ -f "$SRC_TGZ" ]; then
20− cabal install "$SRC_TGZ";
21− else
22− echo "expected '$SRC_TGZ' not found";
23− exit 1;
24− fi
64+ - if [ -f configure.ac ]; then autoreconf -i; fi
65+ - rm -rf .ghc.environment.* dist/
66+ - cabal sdist # test that a source-distribution can be generated
67+ - cd dist/
68+ - SRCTAR=(${PKGNAME}-*.tar.gz)
69+ - SRC_BASENAME="${SRCTAR/%.tar.gz}"
70+ - tar -xvf "./$SRC_BASENAME.tar.gz"
71+ - cd "$SRC_BASENAME/"
72+## from here on, CWD is inside the extracted source-tarball
73+ - rm -fv cabal.project.local
74+ - "echo 'packages: .' > cabal.project"
75+ # this builds all libraries and executables (without tests/benchmarks)
76+ - rm -f cabal.project.freeze
77+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
78+ # this builds all libraries and executables (including tests/benchmarks)
79+ # - rm -rf ./dist-newstyle
80+
81+ # Build with installed constraints for packages in global-db
82+ - if $INSTALLED; then
83+ echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh;
84+ else echo "Not building with installed constraints"; fi
85+
86+ # build & run tests, build benchmarks
87+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all
88+ - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} all; fi
89+
90+ # haddock
91+ - rm -rf ./dist-newstyle
92+ - if $HADDOCK; then cabal new-haddock -w ${HC} --disable-tests --disable-benchmarks all; else echo "Skipping haddock generation";fi
93+
94+# REGENDATA ["hjsonschema.cabal"]
95+# EOF
JSON-Schema-Test-Suitedeleted−1

Submodule changed.

JSON-Schema-Test-Suite/.gitignorenew file+1
@@ -0,0 +1 @@
1+TODO
JSON-Schema-Test-Suite/.travis.ymlnew file+9
@@ -0,0 +1,9 @@
1+language: python
2+python: "2.7"
3+node_js: "6"
4+install:
5+ - pip install tox
6+ - npm install
7+script:
8+ - tox
9+ - npm test
JSON-Schema-Test-Suite/LICENSEnew file+19
@@ -0,0 +1,19 @@
1+Copyright (c) 2012 Julian Berman
2+
3+Permission is hereby granted, free of charge, to any person obtaining a copy
4+of this software and associated documentation files (the "Software"), to deal
5+in the Software without restriction, including without limitation the rights
6+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+copies of the Software, and to permit persons to whom the Software is
8+furnished to do so, subject to the following conditions:
9+
10+The above copyright notice and this permission notice shall be included in
11+all copies or substantial portions of the Software.
12+
13+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+THE SOFTWARE.
JSON-Schema-Test-Suite/README.mdnew file+169
@@ -0,0 +1,169 @@
1+JSON Schema Test Suite [![Build Status](https://travis-ci.org/json-schema-org/JSON-Schema-Test-Suite.svg?branch=master)](https://travis-ci.org/json-schema-org/JSON-Schema-Test-Suite)
2+======================
3+
4+This repository contains a set of JSON objects that implementors of JSON Schema
5+validation libraries can use to test their validators.
6+
7+It is meant to be language agnostic and should require only a JSON parser.
8+
9+The conversion of the JSON objects into tests within your test framework of
10+choice is still the job of the validator implementor.
11+
12+Structure of a Test
13+-------------------
14+
15+If you're going to use this suite, you need to know how tests are laid out. The
16+tests are contained in the `tests` directory at the root of this repository.
17+
18+Inside that directory is a subdirectory for each draft or version of the
19+schema. We'll use `draft3` as an example.
20+
21+If you look inside the draft directory, there are a number of `.json` files,
22+which logically group a set of test cases together. Often the grouping is by
23+property under test, but not always, especially within optional test files
24+(discussed below).
25+
26+Inside each `.json` file is a single array containing objects. It's easiest to
27+illustrate the structure of these with an example:
28+
29+```json
30+ {
31+ "description": "the description of the test case",
32+ "schema": {"the schema that should" : "be validated against"},
33+ "tests": [
34+ {
35+ "description": "a specific test of a valid instance",
36+ "data": "the instance",
37+ "valid": true
38+ },
39+ {
40+ "description": "another specific test this time, invalid",
41+ "data": 15,
42+ "valid": false
43+ }
44+ ]
45+ }
46+```
47+
48+So a description, a schema, and some tests, where tests is an array containing
49+one or more objects with descriptions, data, and a boolean indicating whether
50+they should be valid or invalid.
51+
52+Coverage
53+--------
54+
55+Draft 3 and 4 should have full coverage. If you see anything missing or think
56+there is a useful test missing, please send a pull request or open an issue.
57+
58+Who Uses the Test Suite
59+-----------------------
60+
61+This suite is being used by:
62+
63+### Coffeescript ###
64+
65+* [jsck](https://github.com/pandastrike/jsck)
66+
67+### C++ ###
68+
69+* [Modern C++ JSON schema validator](https://github.com/pboettch/json-schema-validator)
70+
71+### Dart ###
72+
73+* [json_schema](https://github.com/patefacio/json_schema)
74+
75+### Elixir ###
76+
77+* [ex_json_schema](https://github.com/jonasschmidt/ex_json_schema)
78+
79+### Erlang ###
80+
81+* [jesse](https://github.com/klarna/jesse)
82+
83+### Go ###
84+
85+* [gojsonschema](https://github.com/sigu-399/gojsonschema)
86+* [validate-json](https://github.com/cesanta/validate-json)
87+
88+### Haskell ###
89+
90+* [aeson-schema](https://github.com/timjb/aeson-schema)
91+* [hjsonschema](https://github.com/seagreen/hjsonschema)
92+
93+### Java ###
94+
95+* [json-schema-validator](https://github.com/daveclayton/json-schema-validator)
96+* [everit-org/json-schema](https://github.com/everit-org/json-schema)
97+* [networknt/json-schema-validator](https://github.com/networknt/json-schema-validator)
98+
99+### JavaScript ###
100+
101+* [json-schema-benchmark](https://github.com/Muscula/json-schema-benchmark)
102+* [direct-schema](https://github.com/IreneKnapp/direct-schema)
103+* [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid)
104+* [jassi](https://github.com/iclanzan/jassi)
105+* [JaySchema](https://github.com/natesilva/jayschema)
106+* [json-schema-valid](https://github.com/ericgj/json-schema-valid)
107+* [Jsonary](https://github.com/jsonary-js/jsonary)
108+* [jsonschema](https://github.com/tdegrunt/jsonschema)
109+* [request-validator](https://github.com/bugventure/request-validator)
110+* [skeemas](https://github.com/Prestaul/skeemas)
111+* [tv4](https://github.com/geraintluff/tv4)
112+* [z-schema](https://github.com/zaggino/z-schema)
113+* [jsen](https://github.com/bugventure/jsen)
114+* [ajv](https://github.com/epoberezkin/ajv)
115+* [djv](https://github.com/korzio/djv)
116+
117+### Node.js ###
118+
119+The JSON Schema Test Suite is also available as an
120+[npm](https://www.npmjs.com/package/json-schema-test-suite) package.
121+Node-specific support is maintained on the [node branch](https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/node).
122+See [NODE-README.md](https://github.com/json-schema-org/JSON-Schema-Test-Suite/blob/node/NODE-README.md)
123+for more information.
124+
125+### .NET ###
126+
127+* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema)
128+* [Manatee.Json](https://github.com/gregsdennis/Manatee.Json)
129+
130+### PHP ###
131+
132+* [json-schema](https://github.com/justinrainbow/json-schema)
133+* [json-guard](https://github.com/thephpleague/json-guard)
134+
135+### Python ###
136+
137+* [jsonschema](https://github.com/Julian/jsonschema)
138+
139+### Ruby ###
140+
141+* [json-schema](https://github.com/hoxworth/json-schema)
142+
143+### Rust ###
144+
145+* [valico](https://github.com/rustless/valico)
146+
147+### Swift ###
148+
149+* [JSONSchema](https://github.com/kylef/JSONSchema.swift)
150+
151+### Clojure ###
152+
153+* [json-schema](https://github.com/tatut/json-schema)
154+
155+### PostgreSQL ###
156+
157+* [postgres-json-schema](https://github.com/gavinwahl/postgres-json-schema)
158+
159+If you use it as well, please fork and send a pull request adding yourself to
160+the list :).
161+
162+Contributing
163+------------
164+
165+If you see something missing or incorrect, a pull request is most welcome!
166+
167+There are some sanity checks in place for testing the test suite. You can run
168+them with `bin/jsonschema_suite check && npm test` or `tox && npm test`. They will be run automatically by
169+[Travis CI](https://travis-ci.org/) as well.
JSON-Schema-Test-Suite/bin/jsonschema_suitenew file+258
@@ -0,0 +1,258 @@
1+#! /usr/bin/env python
2+from __future__ import print_function
3+import sys
4+import textwrap
5+
6+try:
7+ import argparse
8+except ImportError:
9+ print(textwrap.dedent("""
10+ The argparse library could not be imported. jsonschema_suite requires
11+ either Python 2.7 or for you to install argparse. You can do so by
12+ running `pip install argparse`, `easy_install argparse` or by
13+ downloading argparse and running `python2.6 setup.py install`.
14+
15+ See https://pypi.python.org/pypi/argparse for details.
16+ """.strip("\n")))
17+ sys.exit(1)
18+
19+import errno
20+import fnmatch
21+import json
22+import os
23+import random
24+import shutil
25+import unittest
26+import warnings
27+
28+if getattr(unittest, "skipIf", None) is None:
29+ unittest.skipIf = lambda cond, msg : lambda fn : fn
30+
31+try:
32+ import jsonschema
33+except ImportError:
34+ jsonschema = None
35+else:
36+ validators = getattr(
37+ jsonschema.validators, "validators", jsonschema.validators
38+ )
39+
40+
41+ROOT_DIR = os.path.join(
42+ os.path.dirname(__file__), os.pardir).rstrip("__pycache__")
43+SUITE_ROOT_DIR = os.path.join(ROOT_DIR, "tests")
44+
45+REMOTES = {
46+ "integer.json": {"type": "integer"},
47+ "subSchemas.json": {
48+ "integer": {"type": "integer"},
49+ "refToInteger": {"$ref": "#/integer"},
50+ },
51+ "folder/folderInteger.json": {"type": "integer"}
52+}
53+REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
54+
55+with open(os.path.join(ROOT_DIR, "test-schema.json")) as schema:
56+ TESTSUITE_SCHEMA = json.load(schema)
57+
58+def files(paths):
59+ for path in paths:
60+ with open(path) as test_file:
61+ yield json.load(test_file)
62+
63+
64+def groups(paths):
65+ for test_file in files(paths):
66+ for group in test_file:
67+ yield group
68+
69+
70+def cases(paths):
71+ for test_group in groups(paths):
72+ for test in test_group["tests"]:
73+ test["schema"] = test_group["schema"]
74+ yield test
75+
76+
77+def collect(root_dir):
78+ for root, dirs, files in os.walk(root_dir):
79+ for filename in fnmatch.filter(files, "*.json"):
80+ yield os.path.join(root, filename)
81+
82+
83+class SanityTests(unittest.TestCase):
84+ @classmethod
85+ def setUpClass(cls):
86+ print("Looking for tests in %s" % SUITE_ROOT_DIR)
87+ cls.test_files = list(collect(SUITE_ROOT_DIR))
88+ print("Found %s test files" % len(cls.test_files))
89+ assert cls.test_files, "Didn't find the test files!"
90+
91+ def test_all_files_are_valid_json(self):
92+ for path in self.test_files:
93+ with open(path) as test_file:
94+ try:
95+ json.load(test_file)
96+ except ValueError as error:
97+ self.fail("%s contains invalid JSON (%s)" % (path, error))
98+
99+ def test_all_descriptions_have_reasonable_length(self):
100+ for case in cases(self.test_files):
101+ descript = case["description"]
102+ self.assertLess(
103+ len(descript),
104+ 60,
105+ "%r is too long! (keep it to less than 60 chars)" % (descript,)
106+ )
107+
108+ def test_all_descriptions_are_unique(self):
109+ for group in groups(self.test_files):
110+ descriptions = set(test["description"] for test in group["tests"])
111+ self.assertEqual(
112+ len(descriptions),
113+ len(group["tests"]),
114+ "%r contains a duplicate description" % (group,)
115+ )
116+
117+ @unittest.skipIf(jsonschema is None, "Validation library not present!")
118+ def test_all_schemas_are_valid(self):
119+ for schema in os.listdir(SUITE_ROOT_DIR):
120+ schema_validator = validators.get(schema)
121+ if schema_validator is not None:
122+ test_files = collect(os.path.join(SUITE_ROOT_DIR, schema))
123+ for case in cases(test_files):
124+ try:
125+ schema_validator.check_schema(case["schema"])
126+ except jsonschema.SchemaError as error:
127+ self.fail("%s contains an invalid schema (%s)" %
128+ (case, error))
129+ else:
130+ warnings.warn("No schema validator for %s" % schema)
131+
132+ @unittest.skipIf(jsonschema is None, "Validation library not present!")
133+ def test_suites_are_valid(self):
134+ validator = jsonschema.Draft3Validator(TESTSUITE_SCHEMA)
135+ for tests in files(self.test_files):
136+ try:
137+ validator.validate(tests)
138+ except jsonschema.ValidationError as error:
139+ self.fail(str(error))
140+
141+ def test_remote_schemas_are_updated(self):
142+ for url, schema in REMOTES.items():
143+ filepath = os.path.join(REMOTES_DIR, url)
144+ with open(filepath) as schema_file:
145+ self.assertEqual(json.load(schema_file), schema)
146+
147+
148+def main(arguments):
149+ if arguments.command == "check":
150+ suite = unittest.TestLoader().loadTestsFromTestCase(SanityTests)
151+ result = unittest.TextTestRunner(verbosity=2).run(suite)
152+ sys.exit(not result.wasSuccessful())
153+ elif arguments.command == "flatten":
154+ selected_cases = [case for case in cases(collect(arguments.version))]
155+
156+ if arguments.randomize:
157+ random.shuffle(selected_cases)
158+
159+ json.dump(selected_cases, sys.stdout, indent=4, sort_keys=True)
160+ elif arguments.command == "remotes":
161+ json.dump(REMOTES, sys.stdout, indent=4, sort_keys=True)
162+ elif arguments.command == "dump_remotes":
163+ if arguments.update:
164+ shutil.rmtree(arguments.out_dir, ignore_errors=True)
165+
166+ try:
167+ os.makedirs(arguments.out_dir)
168+ except OSError as e:
169+ if e.errno == errno.EEXIST:
170+ print("%s already exists. Aborting." % arguments.out_dir)
171+ sys.exit(1)
172+ raise
173+
174+ for url, schema in REMOTES.items():
175+ filepath = os.path.join(arguments.out_dir, url)
176+
177+ try:
178+ os.makedirs(os.path.dirname(filepath))
179+ except OSError as e:
180+ if e.errno != errno.EEXIST:
181+ raise
182+
183+ with open(filepath, "wb") as out_file:
184+ json.dump(schema, out_file, indent=4, sort_keys=True)
185+ elif arguments.command == "serve":
186+ try:
187+ from flask import Flask, jsonify
188+ except ImportError:
189+ print(textwrap.dedent("""
190+ The Flask library is required to serve the remote schemas.
191+
192+ You can install it by running `pip install Flask`.
193+
194+ Alternatively, see the `jsonschema_suite remotes` or
195+ `jsonschema_suite dump_remotes` commands to create static files
196+ that can be served with your own web server.
197+ """.strip("\n")))
198+ sys.exit(1)
199+
200+ app = Flask(__name__)
201+
202+ @app.route("/<path:path>")
203+ def serve_path(path):
204+ if path in REMOTES:
205+ return jsonify(REMOTES[path])
206+ return "Document does not exist.", 404
207+
208+ app.run(port=1234)
209+
210+
211+parser = argparse.ArgumentParser(
212+ description="JSON Schema Test Suite utilities",
213+)
214+subparsers = parser.add_subparsers(help="utility commands", dest="command")
215+
216+check = subparsers.add_parser("check", help="Sanity check the test suite.")
217+
218+flatten = subparsers.add_parser(
219+ "flatten",
220+ help="Output a flattened file containing a selected version's test cases."
221+)
222+flatten.add_argument(
223+ "--randomize",
224+ action="store_true",
225+ help="Randomize the order of the outputted cases.",
226+)
227+flatten.add_argument(
228+ "version", help="The directory containing the version to output",
229+)
230+
231+remotes = subparsers.add_parser(
232+ "remotes",
233+ help="Output the expected URLs and their associated schemas for remote "
234+ "ref tests as a JSON object."
235+)
236+
237+dump_remotes = subparsers.add_parser(
238+ "dump_remotes", help="Dump the remote ref schemas into a file tree",
239+)
240+dump_remotes.add_argument(
241+ "--update",
242+ action="store_true",
243+ help="Update the remotes in an existing directory.",
244+)
245+dump_remotes.add_argument(
246+ "--out-dir",
247+ default=REMOTES_DIR,
248+ type=os.path.abspath,
249+ help="The output directory to create as the root of the file tree",
250+)
251+
252+serve = subparsers.add_parser(
253+ "serve",
254+ help="Start a webserver to serve schemas used by remote ref tests."
255+)
256+
257+if __name__ == "__main__":
258+ main(parser.parse_args())
JSON-Schema-Test-Suite/index.jsnew file+32
@@ -0,0 +1,32 @@
1+'use strict';
2+
3+var Ajv = require('ajv');
4+var jsonSchemaTest = require('json-schema-test');
5+var assert = require('assert');
6+
7+var refs = {
8+ 'http://localhost:1234/integer.json': require('./remotes/integer.json'),
9+ 'http://localhost:1234/subSchemas.json': require('./remotes/subSchemas.json'),
10+ 'http://localhost:1234/folder/folderInteger.json': require('./remotes/folder/folderInteger.json'),
11+ 'http://localhost:1234/name.json': require('./remotes/name.json')
12+};
13+
14+runTest(4);
15+runTest(6);
16+
17+function runTest(draft) {
18+ var opts = {};
19+ if (draft == 4) opts.meta = false;
20+ var ajv = new Ajv(opts);
21+ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json'));
22+ if (draft == 4) ajv._opts.defaultMeta = 'http://json-schema.org/draft-04/schema#';
23+ for (var uri in refs) ajv.addSchema(refs[uri], uri);
24+
25+ jsonSchemaTest(ajv, {
26+ description: 'Test suite draft-0' + draft,
27+ suites: {tests: './tests/draft' + draft + '/{**/,}*.json'},
28+ skip: draft == 4 ? ['optional/zeroTerminatedFloats'] : [],
29+ cwd: __dirname,
30+ hideFolder: 'tests/'
31+ });
32+}
JSON-Schema-Test-Suite/package.jsonnew file+28
@@ -0,0 +1,28 @@
1+{
2+ "name": "json-schema-test-suite",
3+ "version": "0.1.0",
4+ "description": "A language agnostic test suite for the JSON Schema specifications",
5+ "main": "index.js",
6+ "scripts": {
7+ "test": "mocha index.js -R spec"
8+ },
9+ "repository": {
10+ "type": "git",
11+ "url": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite.git"
12+ },
13+ "keywords": [
14+ "json-schema",
15+ "tests"
16+ ],
17+ "author": "http://json-schema.org",
18+ "license": "MIT",
19+ "bugs": {
20+ "url": "https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues"
21+ },
22+ "homepage": "https://github.com/json-schema-org/JSON-Schema-Test-Suite#readme",
23+ "devDependencies": {
24+ "ajv": "^5.0.4-beta.0",
25+ "json-schema-test": "^1.3.0",
26+ "mocha": "^3.2.0"
27+ }
28+}
JSON-Schema-Test-Suite/remotes/folder/folderInteger.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "type": "integer"
3+}
JSON-Schema-Test-Suite/remotes/integer.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "type": "integer"
3+}
JSON-Schema-Test-Suite/remotes/name.jsonnew file+11
@@ -0,0 +1,11 @@
1+{
2+ "definitions": {
3+ "orNull": {
4+ "anyOf": [
5+ {"type": "null"},
6+ {"$ref": "#"}
7+ ]
8+ }
9+ },
10+ "type": "string"
11+}
JSON-Schema-Test-Suite/remotes/subSchemas.jsonnew file+8
@@ -0,0 +1,8 @@
1+{
2+ "integer": {
3+ "type": "integer"
4+ },
5+ "refToInteger": {
6+ "$ref": "#/integer"
7+ }
8+}
JSON-Schema-Test-Suite/shell.nixnew file+8
@@ -0,0 +1,8 @@
1+with import <nixpkgs> {};
2+
3+stdenv.mkDerivation {
4+ name = "none";
5+ buildInputs = [
6+ pythonPackages.jsonschema
7+ ];
8+}
JSON-Schema-Test-Suite/test-schema.jsonnew file+28
@@ -0,0 +1,28 @@
1+{
2+ "$schema": "http://json-schema.org/draft-04/schema#",
3+ "type": "array",
4+ "items": {
5+ "type": "object",
6+ "required": ["description", "schema", "tests"],
7+ "properties": {
8+ "description": {"type": "string"},
9+ "schema": {},
10+ "tests": {
11+ "type": "array",
12+ "items": {
13+ "type": "object",
14+ "required": ["description", "data", "valid"],
15+ "properties": {
16+ "description": {"type": "string"},
17+ "data": {},
18+ "valid": {"type": "boolean"}
19+ },
20+ "additionalProperties": false
21+ },
22+ "minItems": 1
23+ }
24+ },
25+ "additionalProperties": false,
26+ "minItems": 1
27+ }
28+}
JSON-Schema-Test-Suite/tests/draft3/additionalItems.jsonnew file+82
@@ -0,0 +1,82 @@
1+[
2+ {
3+ "description": "additionalItems as schema",
4+ "schema": {
5+ "items": [],
6+ "additionalItems": {"type": "integer"}
7+ },
8+ "tests": [
9+ {
10+ "description": "additional items match schema",
11+ "data": [ 1, 2, 3, 4 ],
12+ "valid": true
13+ },
14+ {
15+ "description": "additional items do not match schema",
16+ "data": [ 1, 2, 3, "foo" ],
17+ "valid": false
18+ }
19+ ]
20+ },
21+ {
22+ "description": "items is schema, no additionalItems",
23+ "schema": {
24+ "items": {},
25+ "additionalItems": false
26+ },
27+ "tests": [
28+ {
29+ "description": "all items match schema",
30+ "data": [ 1, 2, 3, 4, 5 ],
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "array of items with no additionalItems",
37+ "schema": {
38+ "items": [{}, {}, {}],
39+ "additionalItems": false
40+ },
41+ "tests": [
42+ {
43+ "description": "no additional items present",
44+ "data": [ 1, 2, 3 ],
45+ "valid": true
46+ },
47+ {
48+ "description": "additional items are not permitted",
49+ "data": [ 1, 2, 3, 4 ],
50+ "valid": false
51+ }
52+ ]
53+ },
54+ {
55+ "description": "additionalItems as false without items",
56+ "schema": {"additionalItems": false},
57+ "tests": [
58+ {
59+ "description":
60+ "items defaults to empty schema so everything is valid",
61+ "data": [ 1, 2, 3, 4, 5 ],
62+ "valid": true
63+ },
64+ {
65+ "description": "ignores non-arrays",
66+ "data": {"foo" : "bar"},
67+ "valid": true
68+ }
69+ ]
70+ },
71+ {
72+ "description": "additionalItems are allowed by default",
73+ "schema": {"items": []},
74+ "tests": [
75+ {
76+ "description": "only the first items are validated",
77+ "data": [1, "foo", false],
78+ "valid": true
79+ }
80+ ]
81+ }
82+]
JSON-Schema-Test-Suite/tests/draft3/additionalProperties.jsonnew file+88
@@ -0,0 +1,88 @@
1+[
2+ {
3+ "description":
4+ "additionalProperties being false does not allow other properties",
5+ "schema": {
6+ "properties": {"foo": {}, "bar": {}},
7+ "patternProperties": { "^v": {} },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "no additional properties is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "an additional property is invalid",
18+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": [1, 2, 3],
24+ "valid": true
25+ },
26+ {
27+ "description": "patternProperties are not additional properties",
28+ "data": {"foo":1, "vroom": 2},
29+ "valid": true
30+ }
31+ ]
32+ },
33+ {
34+ "description":
35+ "additionalProperties allows a schema which should validate",
36+ "schema": {
37+ "properties": {"foo": {}, "bar": {}},
38+ "additionalProperties": {"type": "boolean"}
39+ },
40+ "tests": [
41+ {
42+ "description": "no additional properties is valid",
43+ "data": {"foo": 1},
44+ "valid": true
45+ },
46+ {
47+ "description": "an additional valid property is valid",
48+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
49+ "valid": true
50+ },
51+ {
52+ "description": "an additional invalid property is invalid",
53+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
54+ "valid": false
55+ }
56+ ]
57+ },
58+ {
59+ "description":
60+ "additionalProperties can exist by itself",
61+ "schema": {
62+ "additionalProperties": {"type": "boolean"}
63+ },
64+ "tests": [
65+ {
66+ "description": "an additional valid property is valid",
67+ "data": {"foo" : true},
68+ "valid": true
69+ },
70+ {
71+ "description": "an additional invalid property is invalid",
72+ "data": {"foo" : 1},
73+ "valid": false
74+ }
75+ ]
76+ },
77+ {
78+ "description": "additionalProperties are allowed by default",
79+ "schema": {"properties": {"foo": {}, "bar": {}}},
80+ "tests": [
81+ {
82+ "description": "additional properties are allowed",
83+ "data": {"foo": 1, "bar": 2, "quux": true},
84+ "valid": true
85+ }
86+ ]
87+ }
88+]
JSON-Schema-Test-Suite/tests/draft3/default.jsonnew file+49
@@ -0,0 +1,49 @@
1+[
2+ {
3+ "description": "invalid type for default",
4+ "schema": {
5+ "properties": {
6+ "foo": {
7+ "type": "integer",
8+ "default": []
9+ }
10+ }
11+ },
12+ "tests": [
13+ {
14+ "description": "valid when property is specified",
15+ "data": {"foo": 13},
16+ "valid": true
17+ },
18+ {
19+ "description": "still valid when the invalid default is used",
20+ "data": {},
21+ "valid": true
22+ }
23+ ]
24+ },
25+ {
26+ "description": "invalid string value for default",
27+ "schema": {
28+ "properties": {
29+ "bar": {
30+ "type": "string",
31+ "minLength": 4,
32+ "default": "bad"
33+ }
34+ }
35+ },
36+ "tests": [
37+ {
38+ "description": "valid when property is specified",
39+ "data": {"bar": "good"},
40+ "valid": true
41+ },
42+ {
43+ "description": "still valid when the invalid default is used",
44+ "data": {},
45+ "valid": true
46+ }
47+ ]
48+ }
49+]
JSON-Schema-Test-Suite/tests/draft3/dependencies.jsonnew file+108
@@ -0,0 +1,108 @@
1+[
2+ {
3+ "description": "dependencies",
4+ "schema": {
5+ "dependencies": {"bar": "foo"}
6+ },
7+ "tests": [
8+ {
9+ "description": "neither",
10+ "data": {},
11+ "valid": true
12+ },
13+ {
14+ "description": "nondependant",
15+ "data": {"foo": 1},
16+ "valid": true
17+ },
18+ {
19+ "description": "with dependency",
20+ "data": {"foo": 1, "bar": 2},
21+ "valid": true
22+ },
23+ {
24+ "description": "missing dependency",
25+ "data": {"bar": 2},
26+ "valid": false
27+ },
28+ {
29+ "description": "ignores non-objects",
30+ "data": "foo",
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "multiple dependencies",
37+ "schema": {
38+ "dependencies": {"quux": ["foo", "bar"]}
39+ },
40+ "tests": [
41+ {
42+ "description": "neither",
43+ "data": {},
44+ "valid": true
45+ },
46+ {
47+ "description": "nondependants",
48+ "data": {"foo": 1, "bar": 2},
49+ "valid": true
50+ },
51+ {
52+ "description": "with dependencies",
53+ "data": {"foo": 1, "bar": 2, "quux": 3},
54+ "valid": true
55+ },
56+ {
57+ "description": "missing dependency",
58+ "data": {"foo": 1, "quux": 2},
59+ "valid": false
60+ },
61+ {
62+ "description": "missing other dependency",
63+ "data": {"bar": 1, "quux": 2},
64+ "valid": false
65+ },
66+ {
67+ "description": "missing both dependencies",
68+ "data": {"quux": 1},
69+ "valid": false
70+ }
71+ ]
72+ },
73+ {
74+ "description": "multiple dependencies subschema",
75+ "schema": {
76+ "dependencies": {
77+ "bar": {
78+ "properties": {
79+ "foo": {"type": "integer"},
80+ "bar": {"type": "integer"}
81+ }
82+ }
83+ }
84+ },
85+ "tests": [
86+ {
87+ "description": "valid",
88+ "data": {"foo": 1, "bar": 2},
89+ "valid": true
90+ },
91+ {
92+ "description": "wrong type",
93+ "data": {"foo": "quux", "bar": 2},
94+ "valid": false
95+ },
96+ {
97+ "description": "wrong type other",
98+ "data": {"foo": 2, "bar": "quux"},
99+ "valid": false
100+ },
101+ {
102+ "description": "wrong type both",
103+ "data": {"foo": "quux", "bar": "quux"},
104+ "valid": false
105+ }
106+ ]
107+ }
108+]
JSON-Schema-Test-Suite/tests/draft3/disallow.jsonnew file+80
@@ -0,0 +1,80 @@
1+[
2+ {
3+ "description": "disallow",
4+ "schema": {
5+ "disallow": "integer"
6+ },
7+ "tests": [
8+ {
9+ "description": "allowed",
10+ "data": "foo",
11+ "valid": true
12+ },
13+ {
14+ "description": "disallowed",
15+ "data": 1,
16+ "valid": false
17+ }
18+ ]
19+ },
20+ {
21+ "description": "multiple disallow",
22+ "schema": {
23+ "disallow": ["integer", "boolean"]
24+ },
25+ "tests": [
26+ {
27+ "description": "valid",
28+ "data": "foo",
29+ "valid": true
30+ },
31+ {
32+ "description": "mismatch",
33+ "data": 1,
34+ "valid": false
35+ },
36+ {
37+ "description": "other mismatch",
38+ "data": true,
39+ "valid": false
40+ }
41+ ]
42+ },
43+ {
44+ "description": "multiple disallow subschema",
45+ "schema": {
46+ "disallow":
47+ ["string",
48+ {
49+ "type": "object",
50+ "properties": {
51+ "foo": {
52+ "type": "string"
53+ }
54+ }
55+ }]
56+ },
57+ "tests": [
58+ {
59+ "description": "match",
60+ "data": 1,
61+ "valid": true
62+ },
63+ {
64+ "description": "other match",
65+ "data": {"foo": 1},
66+ "valid": true
67+ },
68+ {
69+ "description": "mismatch",
70+ "data": "foo",
71+ "valid": false
72+ },
73+ {
74+ "description": "other mismatch",
75+ "data": {"foo": "bar"},
76+ "valid": false
77+ }
78+ ]
79+ }
80+]
JSON-Schema-Test-Suite/tests/draft3/divisibleBy.jsonnew file+60
@@ -0,0 +1,60 @@
1+[
2+ {
3+ "description": "by int",
4+ "schema": {"divisibleBy": 2},
5+ "tests": [
6+ {
7+ "description": "int by int",
8+ "data": 10,
9+ "valid": true
10+ },
11+ {
12+ "description": "int by int fail",
13+ "data": 7,
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-numbers",
18+ "data": "foo",
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "by number",
25+ "schema": {"divisibleBy": 1.5},
26+ "tests": [
27+ {
28+ "description": "zero is divisible by anything (except 0)",
29+ "data": 0,
30+ "valid": true
31+ },
32+ {
33+ "description": "4.5 is divisible by 1.5",
34+ "data": 4.5,
35+ "valid": true
36+ },
37+ {
38+ "description": "35 is not divisible by 1.5",
39+ "data": 35,
40+ "valid": false
41+ }
42+ ]
43+ },
44+ {
45+ "description": "by small number",
46+ "schema": {"divisibleBy": 0.0001},
47+ "tests": [
48+ {
49+ "description": "0.0075 is divisible by 0.0001",
50+ "data": 0.0075,
51+ "valid": true
52+ },
53+ {
54+ "description": "0.00751 is not divisible by 0.0001",
55+ "data": 0.00751,
56+ "valid": false
57+ }
58+ ]
59+ }
60+]
JSON-Schema-Test-Suite/tests/draft3/enum.jsonnew file+71
@@ -0,0 +1,71 @@
1+[
2+ {
3+ "description": "simple enum validation",
4+ "schema": {"enum": [1, 2, 3]},
5+ "tests": [
6+ {
7+ "description": "one of the enum is valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "something else is invalid",
13+ "data": 4,
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "heterogeneous enum validation",
20+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
21+ "tests": [
22+ {
23+ "description": "one of the enum is valid",
24+ "data": [],
25+ "valid": true
26+ },
27+ {
28+ "description": "something else is invalid",
29+ "data": null,
30+ "valid": false
31+ },
32+ {
33+ "description": "objects are deep compared",
34+ "data": {"foo": false},
35+ "valid": false
36+ }
37+ ]
38+ },
39+ {
40+ "description": "enums in properties",
41+ "schema": {
42+ "type":"object",
43+ "properties": {
44+ "foo": {"enum":["foo"]},
45+ "bar": {"enum":["bar"], "required":true}
46+ }
47+ },
48+ "tests": [
49+ {
50+ "description": "both properties are valid",
51+ "data": {"foo":"foo", "bar":"bar"},
52+ "valid": true
53+ },
54+ {
55+ "description": "missing optional property is valid",
56+ "data": {"bar":"bar"},
57+ "valid": true
58+ },
59+ {
60+ "description": "missing required property is invalid",
61+ "data": {"foo":"foo"},
62+ "valid": false
63+ },
64+ {
65+ "description": "missing all properties is invalid",
66+ "data": {},
67+ "valid": false
68+ }
69+ ]
70+ }
71+]
JSON-Schema-Test-Suite/tests/draft3/extends.jsonnew file+94
@@ -0,0 +1,94 @@
1+[
2+ {
3+ "description": "extends",
4+ "schema": {
5+ "properties": {"bar": {"type": "integer", "required": true}},
6+ "extends": {
7+ "properties": {
8+ "foo": {"type": "string", "required": true}
9+ }
10+ }
11+ },
12+ "tests": [
13+ {
14+ "description": "extends",
15+ "data": {"foo": "baz", "bar": 2},
16+ "valid": true
17+ },
18+ {
19+ "description": "mismatch extends",
20+ "data": {"foo": "baz"},
21+ "valid": false
22+ },
23+ {
24+ "description": "mismatch extended",
25+ "data": {"bar": 2},
26+ "valid": false
27+ },
28+ {
29+ "description": "wrong type",
30+ "data": {"foo": "baz", "bar": "quux"},
31+ "valid": false
32+ }
33+ ]
34+ },
35+ {
36+ "description": "multiple extends",
37+ "schema": {
38+ "properties": {"bar": {"type": "integer", "required": true}},
39+ "extends" : [
40+ {
41+ "properties": {
42+ "foo": {"type": "string", "required": true}
43+ }
44+ },
45+ {
46+ "properties": {
47+ "baz": {"type": "null", "required": true}
48+ }
49+ }
50+ ]
51+ },
52+ "tests": [
53+ {
54+ "description": "valid",
55+ "data": {"foo": "quux", "bar": 2, "baz": null},
56+ "valid": true
57+ },
58+ {
59+ "description": "mismatch first extends",
60+ "data": {"bar": 2, "baz": null},
61+ "valid": false
62+ },
63+ {
64+ "description": "mismatch second extends",
65+ "data": {"foo": "quux", "bar": 2},
66+ "valid": false
67+ },
68+ {
69+ "description": "mismatch both",
70+ "data": {"bar": 2},
71+ "valid": false
72+ }
73+ ]
74+ },
75+ {
76+ "description": "extends simple types",
77+ "schema": {
78+ "minimum": 20,
79+ "extends": {"maximum": 30}
80+ },
81+ "tests": [
82+ {
83+ "description": "valid",
84+ "data": 25,
85+ "valid": true
86+ },
87+ {
88+ "description": "mismatch extends",
89+ "data": 35,
90+ "valid": false
91+ }
92+ ]
93+ }
94+]
JSON-Schema-Test-Suite/tests/draft3/items.jsonnew file+46
@@ -0,0 +1,46 @@
1+[
2+ {
3+ "description": "a schema given for items",
4+ "schema": {
5+ "items": {"type": "integer"}
6+ },
7+ "tests": [
8+ {
9+ "description": "valid items",
10+ "data": [ 1, 2, 3 ],
11+ "valid": true
12+ },
13+ {
14+ "description": "wrong type of items",
15+ "data": [1, "x"],
16+ "valid": false
17+ },
18+ {
19+ "description": "ignores non-arrays",
20+ "data": {"foo" : "bar"},
21+ "valid": true
22+ }
23+ ]
24+ },
25+ {
26+ "description": "an array of schemas for items",
27+ "schema": {
28+ "items": [
29+ {"type": "integer"},
30+ {"type": "string"}
31+ ]
32+ },
33+ "tests": [
34+ {
35+ "description": "correct types",
36+ "data": [ 1, "foo" ],
37+ "valid": true
38+ },
39+ {
40+ "description": "wrong types",
41+ "data": [ "foo", 1 ],
42+ "valid": false
43+ }
44+ ]
45+ }
46+]
JSON-Schema-Test-Suite/tests/draft3/maxItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maxItems validation",
4+ "schema": {"maxItems": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": [1],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1, 2],
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": [1, 2, 3],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "foobar",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft3/maxLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "maxLength validation",
4+ "schema": {"maxLength": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": "f",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 10,
24+ "valid": true
25+ },
26+ {
27+ "description": "two supplementary Unicode code points is long enough",
28+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
29+ "valid": true
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft3/maximum.jsonnew file+42
@@ -0,0 +1,42 @@
1+[
2+ {
3+ "description": "maximum validation",
4+ "schema": {"maximum": 3.0},
5+ "tests": [
6+ {
7+ "description": "below the maximum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "above the maximum is invalid",
13+ "data": 3.5,
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-numbers",
18+ "data": "x",
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "exclusiveMaximum validation",
25+ "schema": {
26+ "maximum": 3.0,
27+ "exclusiveMaximum": true
28+ },
29+ "tests": [
30+ {
31+ "description": "below the maximum is still valid",
32+ "data": 2.2,
33+ "valid": true
34+ },
35+ {
36+ "description": "boundary point is invalid",
37+ "data": 3.0,
38+ "valid": false
39+ }
40+ ]
41+ }
42+]
JSON-Schema-Test-Suite/tests/draft3/minItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minItems validation",
4+ "schema": {"minItems": 1},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1],
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": [],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft3/minLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "minLength validation",
4+ "schema": {"minLength": 2},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": "foo",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": "f",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 1,
24+ "valid": true
25+ },
26+ {
27+ "description": "one supplementary Unicode code point is not long enough",
28+ "data": "\uD83D\uDCA9",
29+ "valid": false
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft3/minimum.jsonnew file+42
@@ -0,0 +1,42 @@
1+[
2+ {
3+ "description": "minimum validation",
4+ "schema": {"minimum": 1.1},
5+ "tests": [
6+ {
7+ "description": "above the minimum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "below the minimum is invalid",
13+ "data": 0.6,
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-numbers",
18+ "data": "x",
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "exclusiveMinimum validation",
25+ "schema": {
26+ "minimum": 1.1,
27+ "exclusiveMinimum": true
28+ },
29+ "tests": [
30+ {
31+ "description": "above the minimum is still valid",
32+ "data": 1.2,
33+ "valid": true
34+ },
35+ {
36+ "description": "boundary point is invalid",
37+ "data": 1.1,
38+ "valid": false
39+ }
40+ ]
41+ }
42+]
JSON-Schema-Test-Suite/tests/draft3/optional/bignum.jsonnew file+107
@@ -0,0 +1,107 @@
1+[
2+ {
3+ "description": "integer",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "a bignum is an integer",
8+ "data": 12345678910111213141516171819202122232425262728293031,
9+ "valid": true
10+ }
11+ ]
12+ },
13+ {
14+ "description": "number",
15+ "schema": {"type": "number"},
16+ "tests": [
17+ {
18+ "description": "a bignum is a number",
19+ "data": 98249283749234923498293171823948729348710298301928331,
20+ "valid": true
21+ }
22+ ]
23+ },
24+ {
25+ "description": "integer",
26+ "schema": {"type": "integer"},
27+ "tests": [
28+ {
29+ "description": "a negative bignum is an integer",
30+ "data": -12345678910111213141516171819202122232425262728293031,
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "number",
37+ "schema": {"type": "number"},
38+ "tests": [
39+ {
40+ "description": "a negative bignum is a number",
41+ "data": -98249283749234923498293171823948729348710298301928331,
42+ "valid": true
43+ }
44+ ]
45+ },
46+ {
47+ "description": "string",
48+ "schema": {"type": "string"},
49+ "tests": [
50+ {
51+ "description": "a bignum is not a string",
52+ "data": 98249283749234923498293171823948729348710298301928331,
53+ "valid": false
54+ }
55+ ]
56+ },
57+ {
58+ "description": "integer comparison",
59+ "schema": {"maximum": 18446744073709551615},
60+ "tests": [
61+ {
62+ "description": "comparison works for high numbers",
63+ "data": 18446744073709551600,
64+ "valid": true
65+ }
66+ ]
67+ },
68+ {
69+ "description": "float comparison with high precision",
70+ "schema": {
71+ "maximum": 972783798187987123879878123.18878137,
72+ "exclusiveMaximum": true
73+ },
74+ "tests": [
75+ {
76+ "description": "comparison works for high numbers",
77+ "data": 972783798187987123879878123.188781371,
78+ "valid": false
79+ }
80+ ]
81+ },
82+ {
83+ "description": "integer comparison",
84+ "schema": {"minimum": -18446744073709551615},
85+ "tests": [
86+ {
87+ "description": "comparison works for very negative numbers",
88+ "data": -18446744073709551600,
89+ "valid": true
90+ }
91+ ]
92+ },
93+ {
94+ "description": "float comparison with high precision on negative numbers",
95+ "schema": {
96+ "minimum": -972783798187987123879878123.18878137,
97+ "exclusiveMinimum": true
98+ },
99+ "tests": [
100+ {
101+ "description": "comparison works for very negative numbers",
102+ "data": -972783798187987123879878123.188781371,
103+ "valid": false
104+ }
105+ ]
106+ }
107+]
JSON-Schema-Test-Suite/tests/draft3/optional/format.jsonnew file+222
@@ -0,0 +1,222 @@
1+[
2+ {
3+ "description": "validation of regular expressions",
4+ "schema": {"format": "regex"},
5+ "tests": [
6+ {
7+ "description": "a valid regular expression",
8+ "data": "([abc])+\\s+$",
9+ "valid": true
10+ },
11+ {
12+ "description": "a regular expression with unclosed parens is invalid",
13+ "data": "^(abc]",
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "validation of date-time strings",
20+ "schema": {"format": "date-time"},
21+ "tests": [
22+ {
23+ "description": "a valid date-time string",
24+ "data": "1963-06-19T08:30:06.283185Z",
25+ "valid": true
26+ },
27+ {
28+ "description": "an invalid date-time string",
29+ "data": "06/19/1963 08:30:06 PST",
30+ "valid": false
31+ },
32+ {
33+ "description": "only RFC3339 not all of ISO 8601 are valid",
34+ "data": "2013-350T01:01:01",
35+ "valid": false
36+ }
37+ ]
38+ },
39+ {
40+ "description": "validation of date strings",
41+ "schema": {"format": "date"},
42+ "tests": [
43+ {
44+ "description": "a valid date string",
45+ "data": "1963-06-19",
46+ "valid": true
47+ },
48+ {
49+ "description": "an invalid date string",
50+ "data": "06/19/1963",
51+ "valid": false
52+ }
53+ ]
54+ },
55+ {
56+ "description": "validation of time strings",
57+ "schema": {"format": "time"},
58+ "tests": [
59+ {
60+ "description": "a valid time string",
61+ "data": "08:30:06",
62+ "valid": true
63+ },
64+ {
65+ "description": "an invalid time string",
66+ "data": "8:30 AM",
67+ "valid": false
68+ }
69+ ]
70+ },
71+ {
72+ "description": "validation of URIs",
73+ "schema": {"format": "uri"},
74+ "tests": [
75+ {
76+ "description": "a valid URI",
77+ "data": "http://foo.bar/?baz=qux#quux",
78+ "valid": true
79+ },
80+ {
81+ "description": "an invalid protocol-relative URI Reference",
82+ "data": "//foo.bar/?baz=qux#quux",
83+ "valid": false
84+ },
85+ {
86+ "description": "an invalid URI",
87+ "data": "\\\\WINDOWS\\fileshare",
88+ "valid": false
89+ },
90+ {
91+ "description": "an invalid URI though valid URI reference",
92+ "data": "abc",
93+ "valid": false
94+ }
95+ ]
96+ },
97+ {
98+ "description": "validation of e-mail addresses",
99+ "schema": {"format": "email"},
100+ "tests": [
101+ {
102+ "description": "a valid e-mail address",
103+ "data": "joe.bloggs@example.com",
104+ "valid": true
105+ },
106+ {
107+ "description": "an invalid e-mail address",
108+ "data": "2962",
109+ "valid": false
110+ }
111+ ]
112+ },
113+ {
114+ "description": "validation of IP addresses",
115+ "schema": {"format": "ip-address"},
116+ "tests": [
117+ {
118+ "description": "a valid IP address",
119+ "data": "192.168.0.1",
120+ "valid": true
121+ },
122+ {
123+ "description": "an IP address with too many components",
124+ "data": "127.0.0.0.1",
125+ "valid": false
126+ },
127+ {
128+ "description": "an IP address with out-of-range values",
129+ "data": "256.256.256.256",
130+ "valid": false
131+ }
132+ ]
133+ },
134+ {
135+ "description": "validation of IPv6 addresses",
136+ "schema": {"format": "ipv6"},
137+ "tests": [
138+ {
139+ "description": "a valid IPv6 address",
140+ "data": "::1",
141+ "valid": true
142+ },
143+ {
144+ "description": "an IPv6 address with out-of-range values",
145+ "data": "12345::",
146+ "valid": false
147+ },
148+ {
149+ "description": "an IPv6 address with too many components",
150+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
151+ "valid": false
152+ },
153+ {
154+ "description": "an IPv6 address containing illegal characters",
155+ "data": "::laptop",
156+ "valid": false
157+ }
158+ ]
159+ },
160+ {
161+ "description": "validation of host names",
162+ "schema": {"format": "host-name"},
163+ "tests": [
164+ {
165+ "description": "a valid host name",
166+ "data": "www.example.com",
167+ "valid": true
168+ },
169+ {
170+ "description": "a host name starting with an illegal character",
171+ "data": "-a-host-name-that-starts-with--",
172+ "valid": false
173+ },
174+ {
175+ "description": "a host name containing illegal characters",
176+ "data": "not_a_valid_host_name",
177+ "valid": false
178+ },
179+ {
180+ "description": "a host name with a component too long",
181+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
182+ "valid": false
183+ }
184+ ]
185+ },
186+ {
187+ "description": "validation of CSS colors",
188+ "schema": {"format": "color"},
189+ "tests": [
190+ {
191+ "description": "a valid CSS color name",
192+ "data": "fuchsia",
193+ "valid": true
194+ },
195+ {
196+ "description": "a valid six-digit CSS color code",
197+ "data": "#CC8899",
198+ "valid": true
199+ },
200+ {
201+ "description": "a valid three-digit CSS color code",
202+ "data": "#C89",
203+ "valid": true
204+ },
205+ {
206+ "description": "an invalid CSS color code",
207+ "data": "#00332520",
208+ "valid": false
209+ },
210+ {
211+ "description": "an invalid CSS color name",
212+ "data": "puce",
213+ "valid": false
214+ },
215+ {
216+ "description": "a CSS color name containing invalid characters",
217+ "data": "light_grayish_red-violet",
218+ "valid": false
219+ }
220+ ]
221+ }
222+]
JSON-Schema-Test-Suite/tests/draft3/optional/jsregex.jsonnew file+18
@@ -0,0 +1,18 @@
1+[
2+ {
3+ "description": "ECMA 262 regex dialect recognition",
4+ "schema": { "format": "regex" },
5+ "tests": [
6+ {
7+ "description": "[^] is a valid regex",
8+ "data": "[^]",
9+ "valid": true
10+ },
11+ {
12+ "description": "ECMA 262 has no support for lookbehind",
13+ "data": "(?<=foo)bar",
14+ "valid": false
15+ }
16+ ]
17+ }
18+]
JSON-Schema-Test-Suite/tests/draft3/optional/zeroTerminatedFloats.jsonnew file+15
@@ -0,0 +1,15 @@
1+[
2+ {
3+ "description": "some languages do not distinguish between different types of numeric value",
4+ "schema": {
5+ "type": "integer"
6+ },
7+ "tests": [
8+ {
9+ "description": "a float is not an integer even without fractional part",
10+ "data": 1.0,
11+ "valid": false
12+ }
13+ ]
14+ }
15+]
JSON-Schema-Test-Suite/tests/draft3/pattern.jsonnew file+34
@@ -0,0 +1,34 @@
1+[
2+ {
3+ "description": "pattern validation",
4+ "schema": {"pattern": "^a*$"},
5+ "tests": [
6+ {
7+ "description": "a matching pattern is valid",
8+ "data": "aaa",
9+ "valid": true
10+ },
11+ {
12+ "description": "a non-matching pattern is invalid",
13+ "data": "abc",
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-strings",
18+ "data": true,
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "pattern is not anchored",
25+ "schema": {"pattern": "a+"},
26+ "tests": [
27+ {
28+ "description": "matches a substring",
29+ "data": "xxaayy",
30+ "valid": true
31+ }
32+ ]
33+ }
34+]
JSON-Schema-Test-Suite/tests/draft3/patternProperties.jsonnew file+110
@@ -0,0 +1,110 @@
1+[
2+ {
3+ "description":
4+ "patternProperties validates properties matching a regex",
5+ "schema": {
6+ "patternProperties": {
7+ "f.*o": {"type": "integer"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "a single valid match is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "multiple valid matches is valid",
18+ "data": {"foo": 1, "foooooo" : 2},
19+ "valid": true
20+ },
21+ {
22+ "description": "a single invalid match is invalid",
23+ "data": {"foo": "bar", "fooooo": 2},
24+ "valid": false
25+ },
26+ {
27+ "description": "multiple invalid matches is invalid",
28+ "data": {"foo": "bar", "foooooo" : "baz"},
29+ "valid": false
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": 12,
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description": "multiple simultaneous patternProperties are validated",
40+ "schema": {
41+ "patternProperties": {
42+ "a*": {"type": "integer"},
43+ "aaa*": {"maximum": 20}
44+ }
45+ },
46+ "tests": [
47+ {
48+ "description": "a single valid match is valid",
49+ "data": {"a": 21},
50+ "valid": true
51+ },
52+ {
53+ "description": "a simultaneous match is valid",
54+ "data": {"aaaa": 18},
55+ "valid": true
56+ },
57+ {
58+ "description": "multiple matches is valid",
59+ "data": {"a": 21, "aaaa": 18},
60+ "valid": true
61+ },
62+ {
63+ "description": "an invalid due to one is invalid",
64+ "data": {"a": "bar"},
65+ "valid": false
66+ },
67+ {
68+ "description": "an invalid due to the other is invalid",
69+ "data": {"aaaa": 31},
70+ "valid": false
71+ },
72+ {
73+ "description": "an invalid due to both is invalid",
74+ "data": {"aaa": "foo", "aaaa": 31},
75+ "valid": false
76+ }
77+ ]
78+ },
79+ {
80+ "description": "regexes are not anchored by default and are case sensitive",
81+ "schema": {
82+ "patternProperties": {
83+ "[0-9]{2,}": { "type": "boolean" },
84+ "X_": { "type": "string" }
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "non recognized members are ignored",
90+ "data": { "answer 1": "42" },
91+ "valid": true
92+ },
93+ {
94+ "description": "recognized members are accounted for",
95+ "data": { "a31b": null },
96+ "valid": false
97+ },
98+ {
99+ "description": "regexes are case sensitive",
100+ "data": { "a_x_3": 3 },
101+ "valid": true
102+ },
103+ {
104+ "description": "regexes are case sensitive, 2",
105+ "data": { "a_X_3": 3 },
106+ "valid": false
107+ }
108+ ]
109+ }
110+]
JSON-Schema-Test-Suite/tests/draft3/properties.jsonnew file+92
@@ -0,0 +1,92 @@
1+[
2+ {
3+ "description": "object properties validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {"type": "integer"},
7+ "bar": {"type": "string"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "both properties present and valid is valid",
13+ "data": {"foo": 1, "bar": "baz"},
14+ "valid": true
15+ },
16+ {
17+ "description": "one property invalid is invalid",
18+ "data": {"foo": 1, "bar": {}},
19+ "valid": false
20+ },
21+ {
22+ "description": "both properties invalid is invalid",
23+ "data": {"foo": [], "bar": {}},
24+ "valid": false
25+ },
26+ {
27+ "description": "doesn't invalidate other properties",
28+ "data": {"quux": []},
29+ "valid": true
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": [],
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description":
40+ "properties, patternProperties, additionalProperties interaction",
41+ "schema": {
42+ "properties": {
43+ "foo": {"type": "array", "maxItems": 3},
44+ "bar": {"type": "array"}
45+ },
46+ "patternProperties": {"f.o": {"minItems": 2}},
47+ "additionalProperties": {"type": "integer"}
48+ },
49+ "tests": [
50+ {
51+ "description": "property validates property",
52+ "data": {"foo": [1, 2]},
53+ "valid": true
54+ },
55+ {
56+ "description": "property invalidates property",
57+ "data": {"foo": [1, 2, 3, 4]},
58+ "valid": false
59+ },
60+ {
61+ "description": "patternProperty invalidates property",
62+ "data": {"foo": []},
63+ "valid": false
64+ },
65+ {
66+ "description": "patternProperty validates nonproperty",
67+ "data": {"fxo": [1, 2]},
68+ "valid": true
69+ },
70+ {
71+ "description": "patternProperty invalidates nonproperty",
72+ "data": {"fxo": []},
73+ "valid": false
74+ },
75+ {
76+ "description": "additionalProperty ignores property",
77+ "data": {"bar": []},
78+ "valid": true
79+ },
80+ {
81+ "description": "additionalProperty validates others",
82+ "data": {"quux": 3},
83+ "valid": true
84+ },
85+ {
86+ "description": "additionalProperty invalidates others",
87+ "data": {"quux": "foo"},
88+ "valid": false
89+ }
90+ ]
91+ }
92+]
JSON-Schema-Test-Suite/tests/draft3/ref.jsonnew file+192
@@ -0,0 +1,192 @@
1+[
2+ {
3+ "description": "root pointer ref",
4+ "schema": {
5+ "properties": {
6+ "foo": {"$ref": "#"}
7+ },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "match",
13+ "data": {"foo": false},
14+ "valid": true
15+ },
16+ {
17+ "description": "recursive match",
18+ "data": {"foo": {"foo": false}},
19+ "valid": true
20+ },
21+ {
22+ "description": "mismatch",
23+ "data": {"bar": false},
24+ "valid": false
25+ },
26+ {
27+ "description": "recursive mismatch",
28+ "data": {"foo": {"bar": false}},
29+ "valid": false
30+ }
31+ ]
32+ },
33+ {
34+ "description": "relative pointer ref to object",
35+ "schema": {
36+ "properties": {
37+ "foo": {"type": "integer"},
38+ "bar": {"$ref": "#/properties/foo"}
39+ }
40+ },
41+ "tests": [
42+ {
43+ "description": "match",
44+ "data": {"bar": 3},
45+ "valid": true
46+ },
47+ {
48+ "description": "mismatch",
49+ "data": {"bar": true},
50+ "valid": false
51+ }
52+ ]
53+ },
54+ {
55+ "description": "relative pointer ref to array",
56+ "schema": {
57+ "items": [
58+ {"type": "integer"},
59+ {"$ref": "#/items/0"}
60+ ]
61+ },
62+ "tests": [
63+ {
64+ "description": "match array",
65+ "data": [1, 2],
66+ "valid": true
67+ },
68+ {
69+ "description": "mismatch array",
70+ "data": [1, "foo"],
71+ "valid": false
72+ }
73+ ]
74+ },
75+ {
76+ "description": "escaped pointer ref",
77+ "schema": {
78+ "tilda~field": {"type": "integer"},
79+ "slash/field": {"type": "integer"},
80+ "percent%field": {"type": "integer"},
81+ "properties": {
82+ "tilda": {"$ref": "#/tilda~0field"},
83+ "slash": {"$ref": "#/slash~1field"},
84+ "percent": {"$ref": "#/percent%25field"}
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "slash invalid",
90+ "data": {"slash": "aoeu"},
91+ "valid": false
92+ },
93+ {
94+ "description": "tilda invalid",
95+ "data": {"tilda": "aoeu"},
96+ "valid": false
97+ },
98+ {
99+ "description": "percent invalid",
100+ "data": {"percent": "aoeu"},
101+ "valid": false
102+ },
103+ {
104+ "description": "slash valid",
105+ "data": {"slash": 123},
106+ "valid": true
107+ },
108+ {
109+ "description": "tilda valid",
110+ "data": {"tilda": 123},
111+ "valid": true
112+ },
113+ {
114+ "description": "percent valid",
115+ "data": {"percent": 123},
116+ "valid": true
117+ }
118+ ]
119+ },
120+ {
121+ "description": "nested refs",
122+ "schema": {
123+ "definitions": {
124+ "a": {"type": "integer"},
125+ "b": {"$ref": "#/definitions/a"},
126+ "c": {"$ref": "#/definitions/b"}
127+ },
128+ "$ref": "#/definitions/c"
129+ },
130+ "tests": [
131+ {
132+ "description": "nested ref valid",
133+ "data": 5,
134+ "valid": true
135+ },
136+ {
137+ "description": "nested ref invalid",
138+ "data": "a",
139+ "valid": false
140+ }
141+ ]
142+ },
143+ {
144+ "description": "ref overrides any sibling keywords",
145+ "schema": {
146+ "definitions": {
147+ "reffed": {
148+ "type": "array"
149+ }
150+ },
151+ "properties": {
152+ "foo": {
153+ "$ref": "#/definitions/reffed",
154+ "maxItems": 2
155+ }
156+ }
157+ },
158+ "tests": [
159+ {
160+ "description": "remote ref valid",
161+ "data": { "foo": [] },
162+ "valid": true
163+ },
164+ {
165+ "description": "remote ref valid, maxItems ignored",
166+ "data": { "foo": [ 1, 2, 3] },
167+ "valid": true
168+ },
169+ {
170+ "description": "ref invalid",
171+ "data": { "foo": "string" },
172+ "valid": false
173+ }
174+ ]
175+ },
176+ {
177+ "description": "remote ref, containing refs itself",
178+ "schema": {"$ref": "http://json-schema.org/draft-03/schema#"},
179+ "tests": [
180+ {
181+ "description": "remote ref valid",
182+ "data": {"items": {"type": "integer"}},
183+ "valid": true
184+ },
185+ {
186+ "description": "remote ref invalid",
187+ "data": {"items": {"type": 1}},
188+ "valid": false
189+ }
190+ ]
191+ }
192+]
JSON-Schema-Test-Suite/tests/draft3/refRemote.jsonnew file+74
@@ -0,0 +1,74 @@
1+[
2+ {
3+ "description": "remote ref",
4+ "schema": {"$ref": "http://localhost:1234/integer.json"},
5+ "tests": [
6+ {
7+ "description": "remote ref valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "remote ref invalid",
13+ "data": "a",
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "fragment within remote ref",
20+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
21+ "tests": [
22+ {
23+ "description": "remote fragment valid",
24+ "data": 1,
25+ "valid": true
26+ },
27+ {
28+ "description": "remote fragment invalid",
29+ "data": "a",
30+ "valid": false
31+ }
32+ ]
33+ },
34+ {
35+ "description": "ref within remote ref",
36+ "schema": {
37+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
38+ },
39+ "tests": [
40+ {
41+ "description": "ref within ref valid",
42+ "data": 1,
43+ "valid": true
44+ },
45+ {
46+ "description": "ref within ref invalid",
47+ "data": "a",
48+ "valid": false
49+ }
50+ ]
51+ },
52+ {
53+ "description": "change resolution scope",
54+ "schema": {
55+ "id": "http://localhost:1234/",
56+ "items": {
57+ "id": "folder/",
58+ "items": {"$ref": "folderInteger.json"}
59+ }
60+ },
61+ "tests": [
62+ {
63+ "description": "changed scope ref valid",
64+ "data": [[1]],
65+ "valid": true
66+ },
67+ {
68+ "description": "changed scope ref invalid",
69+ "data": [["a"]],
70+ "valid": false
71+ }
72+ ]
73+ }
74+]
JSON-Schema-Test-Suite/tests/draft3/required.jsonnew file+53
@@ -0,0 +1,53 @@
1+[
2+ {
3+ "description": "required validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {"required" : true},
7+ "bar": {}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "present required property is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "non-present required property is invalid",
18+ "data": {"bar": 1},
19+ "valid": false
20+ }
21+ ]
22+ },
23+ {
24+ "description": "required default validation",
25+ "schema": {
26+ "properties": {
27+ "foo": {}
28+ }
29+ },
30+ "tests": [
31+ {
32+ "description": "not required by default",
33+ "data": {},
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description": "required explicitly false validation",
40+ "schema": {
41+ "properties": {
42+ "foo": {"required": false}
43+ }
44+ },
45+ "tests": [
46+ {
47+ "description": "not required if required is false",
48+ "data": {},
49+ "valid": true
50+ }
51+ ]
52+ }
53+]
JSON-Schema-Test-Suite/tests/draft3/type.jsonnew file+489
@@ -0,0 +1,489 @@
1+[
2+ {
3+ "description": "integer type matches integers",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "an integer is an integer",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "a float is not an integer",
13+ "data": 1.1,
14+ "valid": false
15+ },
16+ {
17+ "description": "a string is not an integer",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "a string is still not an integer, even if it looks like one",
23+ "data": "1",
24+ "valid": false
25+ },
26+ {
27+ "description": "an object is not an integer",
28+ "data": {},
29+ "valid": false
30+ },
31+ {
32+ "description": "an array is not an integer",
33+ "data": [],
34+ "valid": false
35+ },
36+ {
37+ "description": "a boolean is not an integer",
38+ "data": true,
39+ "valid": false
40+ },
41+ {
42+ "description": "null is not an integer",
43+ "data": null,
44+ "valid": false
45+ }
46+ ]
47+ },
48+ {
49+ "description": "number type matches numbers",
50+ "schema": {"type": "number"},
51+ "tests": [
52+ {
53+ "description": "an integer is a number",
54+ "data": 1,
55+ "valid": true
56+ },
57+ {
58+ "description": "a float is a number",
59+ "data": 1.1,
60+ "valid": true
61+ },
62+ {
63+ "description": "a string is not a number",
64+ "data": "foo",
65+ "valid": false
66+ },
67+ {
68+ "description": "a string is still not a number, even if it looks like one",
69+ "data": "1",
70+ "valid": false
71+ },
72+ {
73+ "description": "an object is not a number",
74+ "data": {},
75+ "valid": false
76+ },
77+ {
78+ "description": "an array is not a number",
79+ "data": [],
80+ "valid": false
81+ },
82+ {
83+ "description": "a boolean is not a number",
84+ "data": true,
85+ "valid": false
86+ },
87+ {
88+ "description": "null is not a number",
89+ "data": null,
90+ "valid": false
91+ }
92+ ]
93+ },
94+ {
95+ "description": "string type matches strings",
96+ "schema": {"type": "string"},
97+ "tests": [
98+ {
99+ "description": "1 is not a string",
100+ "data": 1,
101+ "valid": false
102+ },
103+ {
104+ "description": "a float is not a string",
105+ "data": 1.1,
106+ "valid": false
107+ },
108+ {
109+ "description": "a string is a string",
110+ "data": "foo",
111+ "valid": true
112+ },
113+ {
114+ "description": "a string is still a string, even if it looks like a number",
115+ "data": "1",
116+ "valid": true
117+ },
118+ {
119+ "description": "an object is not a string",
120+ "data": {},
121+ "valid": false
122+ },
123+ {
124+ "description": "an array is not a string",
125+ "data": [],
126+ "valid": false
127+ },
128+ {
129+ "description": "a boolean is not a string",
130+ "data": true,
131+ "valid": false
132+ },
133+ {
134+ "description": "null is not a string",
135+ "data": null,
136+ "valid": false
137+ }
138+ ]
139+ },
140+ {
141+ "description": "object type matches objects",
142+ "schema": {"type": "object"},
143+ "tests": [
144+ {
145+ "description": "an integer is not an object",
146+ "data": 1,
147+ "valid": false
148+ },
149+ {
150+ "description": "a float is not an object",
151+ "data": 1.1,
152+ "valid": false
153+ },
154+ {
155+ "description": "a string is not an object",
156+ "data": "foo",
157+ "valid": false
158+ },
159+ {
160+ "description": "an object is an object",
161+ "data": {},
162+ "valid": true
163+ },
164+ {
165+ "description": "an array is not an object",
166+ "data": [],
167+ "valid": false
168+ },
169+ {
170+ "description": "a boolean is not an object",
171+ "data": true,
172+ "valid": false
173+ },
174+ {
175+ "description": "null is not an object",
176+ "data": null,
177+ "valid": false
178+ }
179+ ]
180+ },
181+ {
182+ "description": "array type matches arrays",
183+ "schema": {"type": "array"},
184+ "tests": [
185+ {
186+ "description": "an integer is not an array",
187+ "data": 1,
188+ "valid": false
189+ },
190+ {
191+ "description": "a float is not an array",
192+ "data": 1.1,
193+ "valid": false
194+ },
195+ {
196+ "description": "a string is not an array",
197+ "data": "foo",
198+ "valid": false
199+ },
200+ {
201+ "description": "an object is not an array",
202+ "data": {},
203+ "valid": false
204+ },
205+ {
206+ "description": "an array is an array",
207+ "data": [],
208+ "valid": true
209+ },
210+ {
211+ "description": "a boolean is not an array",
212+ "data": true,
213+ "valid": false
214+ },
215+ {
216+ "description": "null is not an array",
217+ "data": null,
218+ "valid": false
219+ }
220+ ]
221+ },
222+ {
223+ "description": "boolean type matches booleans",
224+ "schema": {"type": "boolean"},
225+ "tests": [
226+ {
227+ "description": "an integer is not a boolean",
228+ "data": 1,
229+ "valid": false
230+ },
231+ {
232+ "description": "a float is not a boolean",
233+ "data": 1.1,
234+ "valid": false
235+ },
236+ {
237+ "description": "a string is not a boolean",
238+ "data": "foo",
239+ "valid": false
240+ },
241+ {
242+ "description": "an object is not a boolean",
243+ "data": {},
244+ "valid": false
245+ },
246+ {
247+ "description": "an array is not a boolean",
248+ "data": [],
249+ "valid": false
250+ },
251+ {
252+ "description": "a boolean is a boolean",
253+ "data": true,
254+ "valid": true
255+ },
256+ {
257+ "description": "null is not a boolean",
258+ "data": null,
259+ "valid": false
260+ }
261+ ]
262+ },
263+ {
264+ "description": "null type matches only the null object",
265+ "schema": {"type": "null"},
266+ "tests": [
267+ {
268+ "description": "an integer is not null",
269+ "data": 1,
270+ "valid": false
271+ },
272+ {
273+ "description": "a float is not null",
274+ "data": 1.1,
275+ "valid": false
276+ },
277+ {
278+ "description": "a string is not null",
279+ "data": "foo",
280+ "valid": false
281+ },
282+ {
283+ "description": "an object is not null",
284+ "data": {},
285+ "valid": false
286+ },
287+ {
288+ "description": "an array is not null",
289+ "data": [],
290+ "valid": false
291+ },
292+ {
293+ "description": "a boolean is not null",
294+ "data": true,
295+ "valid": false
296+ },
297+ {
298+ "description": "null is null",
299+ "data": null,
300+ "valid": true
301+ }
302+ ]
303+ },
304+ {
305+ "description": "any type matches any type",
306+ "schema": {"type": "any"},
307+ "tests": [
308+ {
309+ "description": "any type includes integers",
310+ "data": 1,
311+ "valid": true
312+ },
313+ {
314+ "description": "any type includes float",
315+ "data": 1.1,
316+ "valid": true
317+ },
318+ {
319+ "description": "any type includes string",
320+ "data": "foo",
321+ "valid": true
322+ },
323+ {
324+ "description": "any type includes object",
325+ "data": {},
326+ "valid": true
327+ },
328+ {
329+ "description": "any type includes array",
330+ "data": [],
331+ "valid": true
332+ },
333+ {
334+ "description": "any type includes boolean",
335+ "data": true,
336+ "valid": true
337+ },
338+ {
339+ "description": "any type includes null",
340+ "data": null,
341+ "valid": true
342+ }
343+ ]
344+ },
345+ {
346+ "description": "multiple types can be specified in an array",
347+ "schema": {"type": ["integer", "string"]},
348+ "tests": [
349+ {
350+ "description": "an integer is valid",
351+ "data": 1,
352+ "valid": true
353+ },
354+ {
355+ "description": "a string is valid",
356+ "data": "foo",
357+ "valid": true
358+ },
359+ {
360+ "description": "a float is invalid",
361+ "data": 1.1,
362+ "valid": false
363+ },
364+ {
365+ "description": "an object is invalid",
366+ "data": {},
367+ "valid": false
368+ },
369+ {
370+ "description": "an array is invalid",
371+ "data": [],
372+ "valid": false
373+ },
374+ {
375+ "description": "a boolean is invalid",
376+ "data": true,
377+ "valid": false
378+ },
379+ {
380+ "description": "null is invalid",
381+ "data": null,
382+ "valid": false
383+ }
384+ ]
385+ },
386+ {
387+ "description": "types can include schemas",
388+ "schema": {
389+ "type": [
390+ "array",
391+ {"type": "object"}
392+ ]
393+ },
394+ "tests": [
395+ {
396+ "description": "an integer is invalid",
397+ "data": 1,
398+ "valid": false
399+ },
400+ {
401+ "description": "a string is invalid",
402+ "data": "foo",
403+ "valid": false
404+ },
405+ {
406+ "description": "a float is invalid",
407+ "data": 1.1,
408+ "valid": false
409+ },
410+ {
411+ "description": "an object is valid",
412+ "data": {},
413+ "valid": true
414+ },
415+ {
416+ "description": "an array is valid",
417+ "data": [],
418+ "valid": true
419+ },
420+ {
421+ "description": "a boolean is invalid",
422+ "data": true,
423+ "valid": false
424+ },
425+ {
426+ "description": "null is invalid",
427+ "data": null,
428+ "valid": false
429+ }
430+ ]
431+ },
432+ {
433+ "description":
434+ "when types includes a schema it should fully validate the schema",
435+ "schema": {
436+ "type": [
437+ "integer",
438+ {
439+ "properties": {
440+ "foo": {"type": "null"}
441+ }
442+ }
443+ ]
444+ },
445+ "tests": [
446+ {
447+ "description": "an integer is valid",
448+ "data": 1,
449+ "valid": true
450+ },
451+ {
452+ "description": "an object is valid only if it is fully valid",
453+ "data": {"foo": null},
454+ "valid": true
455+ },
456+ {
457+ "description": "an object is invalid otherwise",
458+ "data": {"foo": "bar"},
459+ "valid": false
460+ }
461+ ]
462+ },
463+ {
464+ "description": "types from separate schemas are merged",
465+ "schema": {
466+ "type": [
467+ {"type": ["string"]},
468+ {"type": ["array", "null"]}
469+ ]
470+ },
471+ "tests": [
472+ {
473+ "description": "an integer is invalid",
474+ "data": 1,
475+ "valid": false
476+ },
477+ {
478+ "description": "a string is valid",
479+ "data": "foo",
480+ "valid": true
481+ },
482+ {
483+ "description": "an array is valid",
484+ "data": [1, 2, 3],
485+ "valid": true
486+ }
487+ ]
488+ }
489+]
JSON-Schema-Test-Suite/tests/draft3/uniqueItems.jsonnew file+79
@@ -0,0 +1,79 @@
1+[
2+ {
3+ "description": "uniqueItems validation",
4+ "schema": {"uniqueItems": true},
5+ "tests": [
6+ {
7+ "description": "unique array of integers is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "non-unique array of integers is invalid",
13+ "data": [1, 1],
14+ "valid": false
15+ },
16+ {
17+ "description": "numbers are unique if mathematically unequal",
18+ "data": [1.0, 1.00, 1],
19+ "valid": false
20+ },
21+ {
22+ "description": "unique array of objects is valid",
23+ "data": [{"foo": "bar"}, {"foo": "baz"}],
24+ "valid": true
25+ },
26+ {
27+ "description": "non-unique array of objects is invalid",
28+ "data": [{"foo": "bar"}, {"foo": "bar"}],
29+ "valid": false
30+ },
31+ {
32+ "description": "unique array of nested objects is valid",
33+ "data": [
34+ {"foo": {"bar" : {"baz" : true}}},
35+ {"foo": {"bar" : {"baz" : false}}}
36+ ],
37+ "valid": true
38+ },
39+ {
40+ "description": "non-unique array of nested objects is invalid",
41+ "data": [
42+ {"foo": {"bar" : {"baz" : true}}},
43+ {"foo": {"bar" : {"baz" : true}}}
44+ ],
45+ "valid": false
46+ },
47+ {
48+ "description": "unique array of arrays is valid",
49+ "data": [["foo"], ["bar"]],
50+ "valid": true
51+ },
52+ {
53+ "description": "non-unique array of arrays is invalid",
54+ "data": [["foo"], ["foo"]],
55+ "valid": false
56+ },
57+ {
58+ "description": "1 and true are unique",
59+ "data": [1, true],
60+ "valid": true
61+ },
62+ {
63+ "description": "0 and false are unique",
64+ "data": [0, false],
65+ "valid": true
66+ },
67+ {
68+ "description": "unique heterogeneous types are valid",
69+ "data": [{}, [1], true, null, 1],
70+ "valid": true
71+ },
72+ {
73+ "description": "non-unique heterogeneous types are invalid",
74+ "data": [{}, [1], true, null, {}, 1],
75+ "valid": false
76+ }
77+ ]
78+ }
79+]
JSON-Schema-Test-Suite/tests/draft4/additionalItems.jsonnew file+87
@@ -0,0 +1,87 @@
1+[
2+ {
3+ "description": "additionalItems as schema",
4+ "schema": {
5+ "items": [{}],
6+ "additionalItems": {"type": "integer"}
7+ },
8+ "tests": [
9+ {
10+ "description": "additional items match schema",
11+ "data": [ null, 2, 3, 4 ],
12+ "valid": true
13+ },
14+ {
15+ "description": "additional items do not match schema",
16+ "data": [ null, 2, 3, "foo" ],
17+ "valid": false
18+ }
19+ ]
20+ },
21+ {
22+ "description": "items is schema, no additionalItems",
23+ "schema": {
24+ "items": {},
25+ "additionalItems": false
26+ },
27+ "tests": [
28+ {
29+ "description": "all items match schema",
30+ "data": [ 1, 2, 3, 4, 5 ],
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "array of items with no additionalItems",
37+ "schema": {
38+ "items": [{}, {}, {}],
39+ "additionalItems": false
40+ },
41+ "tests": [
42+ {
43+ "description": "fewer number of items present",
44+ "data": [ 1, 2 ],
45+ "valid": true
46+ },
47+ {
48+ "description": "equal number of items present",
49+ "data": [ 1, 2, 3 ],
50+ "valid": true
51+ },
52+ {
53+ "description": "additional items are not permitted",
54+ "data": [ 1, 2, 3, 4 ],
55+ "valid": false
56+ }
57+ ]
58+ },
59+ {
60+ "description": "additionalItems as false without items",
61+ "schema": {"additionalItems": false},
62+ "tests": [
63+ {
64+ "description":
65+ "items defaults to empty schema so everything is valid",
66+ "data": [ 1, 2, 3, 4, 5 ],
67+ "valid": true
68+ },
69+ {
70+ "description": "ignores non-arrays",
71+ "data": {"foo" : "bar"},
72+ "valid": true
73+ }
74+ ]
75+ },
76+ {
77+ "description": "additionalItems are allowed by default",
78+ "schema": {"items": [{"type": "integer"}]},
79+ "tests": [
80+ {
81+ "description": "only the first item is validated",
82+ "data": [1, "foo", false],
83+ "valid": true
84+ }
85+ ]
86+ }
87+]
JSON-Schema-Test-Suite/tests/draft4/additionalProperties.jsonnew file+88
@@ -0,0 +1,88 @@
1+[
2+ {
3+ "description":
4+ "additionalProperties being false does not allow other properties",
5+ "schema": {
6+ "properties": {"foo": {}, "bar": {}},
7+ "patternProperties": { "^v": {} },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "no additional properties is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "an additional property is invalid",
18+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": [1, 2, 3],
24+ "valid": true
25+ },
26+ {
27+ "description": "patternProperties are not additional properties",
28+ "data": {"foo":1, "vroom": 2},
29+ "valid": true
30+ }
31+ ]
32+ },
33+ {
34+ "description":
35+ "additionalProperties allows a schema which should validate",
36+ "schema": {
37+ "properties": {"foo": {}, "bar": {}},
38+ "additionalProperties": {"type": "boolean"}
39+ },
40+ "tests": [
41+ {
42+ "description": "no additional properties is valid",
43+ "data": {"foo": 1},
44+ "valid": true
45+ },
46+ {
47+ "description": "an additional valid property is valid",
48+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
49+ "valid": true
50+ },
51+ {
52+ "description": "an additional invalid property is invalid",
53+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
54+ "valid": false
55+ }
56+ ]
57+ },
58+ {
59+ "description":
60+ "additionalProperties can exist by itself",
61+ "schema": {
62+ "additionalProperties": {"type": "boolean"}
63+ },
64+ "tests": [
65+ {
66+ "description": "an additional valid property is valid",
67+ "data": {"foo" : true},
68+ "valid": true
69+ },
70+ {
71+ "description": "an additional invalid property is invalid",
72+ "data": {"foo" : 1},
73+ "valid": false
74+ }
75+ ]
76+ },
77+ {
78+ "description": "additionalProperties are allowed by default",
79+ "schema": {"properties": {"foo": {}, "bar": {}}},
80+ "tests": [
81+ {
82+ "description": "additional properties are allowed",
83+ "data": {"foo": 1, "bar": 2, "quux": true},
84+ "valid": true
85+ }
86+ ]
87+ }
88+]
JSON-Schema-Test-Suite/tests/draft4/allOf.jsonnew file+112
@@ -0,0 +1,112 @@
1+[
2+ {
3+ "description": "allOf",
4+ "schema": {
5+ "allOf": [
6+ {
7+ "properties": {
8+ "bar": {"type": "integer"}
9+ },
10+ "required": ["bar"]
11+ },
12+ {
13+ "properties": {
14+ "foo": {"type": "string"}
15+ },
16+ "required": ["foo"]
17+ }
18+ ]
19+ },
20+ "tests": [
21+ {
22+ "description": "allOf",
23+ "data": {"foo": "baz", "bar": 2},
24+ "valid": true
25+ },
26+ {
27+ "description": "mismatch second",
28+ "data": {"foo": "baz"},
29+ "valid": false
30+ },
31+ {
32+ "description": "mismatch first",
33+ "data": {"bar": 2},
34+ "valid": false
35+ },
36+ {
37+ "description": "wrong type",
38+ "data": {"foo": "baz", "bar": "quux"},
39+ "valid": false
40+ }
41+ ]
42+ },
43+ {
44+ "description": "allOf with base schema",
45+ "schema": {
46+ "properties": {"bar": {"type": "integer"}},
47+ "required": ["bar"],
48+ "allOf" : [
49+ {
50+ "properties": {
51+ "foo": {"type": "string"}
52+ },
53+ "required": ["foo"]
54+ },
55+ {
56+ "properties": {
57+ "baz": {"type": "null"}
58+ },
59+ "required": ["baz"]
60+ }
61+ ]
62+ },
63+ "tests": [
64+ {
65+ "description": "valid",
66+ "data": {"foo": "quux", "bar": 2, "baz": null},
67+ "valid": true
68+ },
69+ {
70+ "description": "mismatch base schema",
71+ "data": {"foo": "quux", "baz": null},
72+ "valid": false
73+ },
74+ {
75+ "description": "mismatch first allOf",
76+ "data": {"bar": 2, "baz": null},
77+ "valid": false
78+ },
79+ {
80+ "description": "mismatch second allOf",
81+ "data": {"foo": "quux", "bar": 2},
82+ "valid": false
83+ },
84+ {
85+ "description": "mismatch both",
86+ "data": {"bar": 2},
87+ "valid": false
88+ }
89+ ]
90+ },
91+ {
92+ "description": "allOf simple types",
93+ "schema": {
94+ "allOf": [
95+ {"maximum": 30},
96+ {"minimum": 20}
97+ ]
98+ },
99+ "tests": [
100+ {
101+ "description": "valid",
102+ "data": 25,
103+ "valid": true
104+ },
105+ {
106+ "description": "mismatch one",
107+ "data": 35,
108+ "valid": false
109+ }
110+ ]
111+ }
112+]
JSON-Schema-Test-Suite/tests/draft4/anyOf.jsonnew file+68
@@ -0,0 +1,68 @@
1+[
2+ {
3+ "description": "anyOf",
4+ "schema": {
5+ "anyOf": [
6+ {
7+ "type": "integer"
8+ },
9+ {
10+ "minimum": 2
11+ }
12+ ]
13+ },
14+ "tests": [
15+ {
16+ "description": "first anyOf valid",
17+ "data": 1,
18+ "valid": true
19+ },
20+ {
21+ "description": "second anyOf valid",
22+ "data": 2.5,
23+ "valid": true
24+ },
25+ {
26+ "description": "both anyOf valid",
27+ "data": 3,
28+ "valid": true
29+ },
30+ {
31+ "description": "neither anyOf valid",
32+ "data": 1.5,
33+ "valid": false
34+ }
35+ ]
36+ },
37+ {
38+ "description": "anyOf with base schema",
39+ "schema": {
40+ "type": "string",
41+ "anyOf" : [
42+ {
43+ "maxLength": 2
44+ },
45+ {
46+ "minLength": 4
47+ }
48+ ]
49+ },
50+ "tests": [
51+ {
52+ "description": "mismatch base schema",
53+ "data": 3,
54+ "valid": false
55+ },
56+ {
57+ "description": "one anyOf valid",
58+ "data": "foobar",
59+ "valid": true
60+ },
61+ {
62+ "description": "both anyOf invalid",
63+ "data": "foo",
64+ "valid": false
65+ }
66+ ]
67+ }
68+]
JSON-Schema-Test-Suite/tests/draft4/default.jsonnew file+49
@@ -0,0 +1,49 @@
1+[
2+ {
3+ "description": "invalid type for default",
4+ "schema": {
5+ "properties": {
6+ "foo": {
7+ "type": "integer",
8+ "default": []
9+ }
10+ }
11+ },
12+ "tests": [
13+ {
14+ "description": "valid when property is specified",
15+ "data": {"foo": 13},
16+ "valid": true
17+ },
18+ {
19+ "description": "still valid when the invalid default is used",
20+ "data": {},
21+ "valid": true
22+ }
23+ ]
24+ },
25+ {
26+ "description": "invalid string value for default",
27+ "schema": {
28+ "properties": {
29+ "bar": {
30+ "type": "string",
31+ "minLength": 4,
32+ "default": "bad"
33+ }
34+ }
35+ },
36+ "tests": [
37+ {
38+ "description": "valid when property is specified",
39+ "data": {"bar": "good"},
40+ "valid": true
41+ },
42+ {
43+ "description": "still valid when the invalid default is used",
44+ "data": {},
45+ "valid": true
46+ }
47+ ]
48+ }
49+]
JSON-Schema-Test-Suite/tests/draft4/definitions.jsonnew file+32
@@ -0,0 +1,32 @@
1+[
2+ {
3+ "description": "valid definition",
4+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
5+ "tests": [
6+ {
7+ "description": "valid definition schema",
8+ "data": {
9+ "definitions": {
10+ "foo": {"type": "integer"}
11+ }
12+ },
13+ "valid": true
14+ }
15+ ]
16+ },
17+ {
18+ "description": "invalid definition",
19+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
20+ "tests": [
21+ {
22+ "description": "invalid definition schema",
23+ "data": {
24+ "definitions": {
25+ "foo": {"type": 1}
26+ }
27+ },
28+ "valid": false
29+ }
30+ ]
31+ }
32+]
JSON-Schema-Test-Suite/tests/draft4/dependencies.jsonnew file+113
@@ -0,0 +1,113 @@
1+[
2+ {
3+ "description": "dependencies",
4+ "schema": {
5+ "dependencies": {"bar": ["foo"]}
6+ },
7+ "tests": [
8+ {
9+ "description": "neither",
10+ "data": {},
11+ "valid": true
12+ },
13+ {
14+ "description": "nondependant",
15+ "data": {"foo": 1},
16+ "valid": true
17+ },
18+ {
19+ "description": "with dependency",
20+ "data": {"foo": 1, "bar": 2},
21+ "valid": true
22+ },
23+ {
24+ "description": "missing dependency",
25+ "data": {"bar": 2},
26+ "valid": false
27+ },
28+ {
29+ "description": "ignores non-objects",
30+ "data": "foo",
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "multiple dependencies",
37+ "schema": {
38+ "dependencies": {"quux": ["foo", "bar"]}
39+ },
40+ "tests": [
41+ {
42+ "description": "neither",
43+ "data": {},
44+ "valid": true
45+ },
46+ {
47+ "description": "nondependants",
48+ "data": {"foo": 1, "bar": 2},
49+ "valid": true
50+ },
51+ {
52+ "description": "with dependencies",
53+ "data": {"foo": 1, "bar": 2, "quux": 3},
54+ "valid": true
55+ },
56+ {
57+ "description": "missing dependency",
58+ "data": {"foo": 1, "quux": 2},
59+ "valid": false
60+ },
61+ {
62+ "description": "missing other dependency",
63+ "data": {"bar": 1, "quux": 2},
64+ "valid": false
65+ },
66+ {
67+ "description": "missing both dependencies",
68+ "data": {"quux": 1},
69+ "valid": false
70+ }
71+ ]
72+ },
73+ {
74+ "description": "multiple dependencies subschema",
75+ "schema": {
76+ "dependencies": {
77+ "bar": {
78+ "properties": {
79+ "foo": {"type": "integer"},
80+ "bar": {"type": "integer"}
81+ }
82+ }
83+ }
84+ },
85+ "tests": [
86+ {
87+ "description": "valid",
88+ "data": {"foo": 1, "bar": 2},
89+ "valid": true
90+ },
91+ {
92+ "description": "no dependency",
93+ "data": {"foo": "quux"},
94+ "valid": true
95+ },
96+ {
97+ "description": "wrong type",
98+ "data": {"foo": "quux", "bar": 2},
99+ "valid": false
100+ },
101+ {
102+ "description": "wrong type other",
103+ "data": {"foo": 2, "bar": "quux"},
104+ "valid": false
105+ },
106+ {
107+ "description": "wrong type both",
108+ "data": {"foo": "quux", "bar": "quux"},
109+ "valid": false
110+ }
111+ ]
112+ }
113+]
JSON-Schema-Test-Suite/tests/draft4/enum.jsonnew file+72
@@ -0,0 +1,72 @@
1+[
2+ {
3+ "description": "simple enum validation",
4+ "schema": {"enum": [1, 2, 3]},
5+ "tests": [
6+ {
7+ "description": "one of the enum is valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "something else is invalid",
13+ "data": 4,
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "heterogeneous enum validation",
20+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
21+ "tests": [
22+ {
23+ "description": "one of the enum is valid",
24+ "data": [],
25+ "valid": true
26+ },
27+ {
28+ "description": "something else is invalid",
29+ "data": null,
30+ "valid": false
31+ },
32+ {
33+ "description": "objects are deep compared",
34+ "data": {"foo": false},
35+ "valid": false
36+ }
37+ ]
38+ },
39+ {
40+ "description": "enums in properties",
41+ "schema": {
42+ "type":"object",
43+ "properties": {
44+ "foo": {"enum":["foo"]},
45+ "bar": {"enum":["bar"]}
46+ },
47+ "required": ["bar"]
48+ },
49+ "tests": [
50+ {
51+ "description": "both properties are valid",
52+ "data": {"foo":"foo", "bar":"bar"},
53+ "valid": true
54+ },
55+ {
56+ "description": "missing optional property is valid",
57+ "data": {"bar":"bar"},
58+ "valid": true
59+ },
60+ {
61+ "description": "missing required property is invalid",
62+ "data": {"foo":"foo"},
63+ "valid": false
64+ },
65+ {
66+ "description": "missing all properties is invalid",
67+ "data": {},
68+ "valid": false
69+ }
70+ ]
71+ }
72+]
JSON-Schema-Test-Suite/tests/draft4/items.jsonnew file+78
@@ -0,0 +1,78 @@
1+[
2+ {
3+ "description": "a schema given for items",
4+ "schema": {
5+ "items": {"type": "integer"}
6+ },
7+ "tests": [
8+ {
9+ "description": "valid items",
10+ "data": [ 1, 2, 3 ],
11+ "valid": true
12+ },
13+ {
14+ "description": "wrong type of items",
15+ "data": [1, "x"],
16+ "valid": false
17+ },
18+ {
19+ "description": "ignores non-arrays",
20+ "data": {"foo" : "bar"},
21+ "valid": true
22+ },
23+ {
24+ "description": "JavaScript pseudo-array is valid",
25+ "data": {
26+ "0": "invalid",
27+ "length": 1
28+ },
29+ "valid": true
30+ }
31+ ]
32+ },
33+ {
34+ "description": "an array of schemas for items",
35+ "schema": {
36+ "items": [
37+ {"type": "integer"},
38+ {"type": "string"}
39+ ]
40+ },
41+ "tests": [
42+ {
43+ "description": "correct types",
44+ "data": [ 1, "foo" ],
45+ "valid": true
46+ },
47+ {
48+ "description": "wrong types",
49+ "data": [ "foo", 1 ],
50+ "valid": false
51+ },
52+ {
53+ "description": "incomplete array of items",
54+ "data": [ 1 ],
55+ "valid": true
56+ },
57+ {
58+ "description": "array with additional items",
59+ "data": [ 1, "foo", true ],
60+ "valid": true
61+ },
62+ {
63+ "description": "empty array",
64+ "data": [ ],
65+ "valid": true
66+ },
67+ {
68+ "description": "JavaScript pseudo-array is valid",
69+ "data": {
70+ "0": "invalid",
71+ "1": "valid",
72+ "length": 2
73+ },
74+ "valid": true
75+ }
76+ ]
77+ }
78+]
JSON-Schema-Test-Suite/tests/draft4/maxItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maxItems validation",
4+ "schema": {"maxItems": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": [1],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1, 2],
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": [1, 2, 3],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "foobar",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft4/maxLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "maxLength validation",
4+ "schema": {"maxLength": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": "f",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 100,
24+ "valid": true
25+ },
26+ {
27+ "description": "two supplementary Unicode code points is long enough",
28+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
29+ "valid": true
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft4/maxProperties.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maxProperties validation",
4+ "schema": {"maxProperties": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": {"foo": 1},
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": {"foo": 1, "bar": 2},
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": {"foo": 1, "bar": 2, "baz": 3},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": "foobar",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft4/maximum.jsonnew file+47
@@ -0,0 +1,47 @@
1+[
2+ {
3+ "description": "maximum validation",
4+ "schema": {"maximum": 3.0},
5+ "tests": [
6+ {
7+ "description": "below the maximum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "boundary point is valid",
13+ "data": 3.0,
14+ "valid": true
15+ },
16+ {
17+ "description": "above the maximum is invalid",
18+ "data": 3.5,
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-numbers",
23+ "data": "x",
24+ "valid": true
25+ }
26+ ]
27+ },
28+ {
29+ "description": "exclusiveMaximum validation",
30+ "schema": {
31+ "maximum": 3.0,
32+ "exclusiveMaximum": true
33+ },
34+ "tests": [
35+ {
36+ "description": "below the maximum is still valid",
37+ "data": 2.2,
38+ "valid": true
39+ },
40+ {
41+ "description": "boundary point is invalid",
42+ "data": 3.0,
43+ "valid": false
44+ }
45+ ]
46+ }
47+]
JSON-Schema-Test-Suite/tests/draft4/minItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minItems validation",
4+ "schema": {"minItems": 1},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1],
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": [],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft4/minLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "minLength validation",
4+ "schema": {"minLength": 2},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": "foo",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": "f",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 1,
24+ "valid": true
25+ },
26+ {
27+ "description": "one supplementary Unicode code point is not long enough",
28+ "data": "\uD83D\uDCA9",
29+ "valid": false
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft4/minProperties.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minProperties validation",
4+ "schema": {"minProperties": 1},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": {"foo": 1, "bar": 2},
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": {},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": "",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft4/minimum.jsonnew file+47
@@ -0,0 +1,47 @@
1+[
2+ {
3+ "description": "minimum validation",
4+ "schema": {"minimum": 1.1},
5+ "tests": [
6+ {
7+ "description": "above the minimum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "boundary point is valid",
13+ "data": 1.1,
14+ "valid": true
15+ },
16+ {
17+ "description": "below the minimum is invalid",
18+ "data": 0.6,
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-numbers",
23+ "data": "x",
24+ "valid": true
25+ }
26+ ]
27+ },
28+ {
29+ "description": "exclusiveMinimum validation",
30+ "schema": {
31+ "minimum": 1.1,
32+ "exclusiveMinimum": true
33+ },
34+ "tests": [
35+ {
36+ "description": "above the minimum is still valid",
37+ "data": 1.2,
38+ "valid": true
39+ },
40+ {
41+ "description": "boundary point is invalid",
42+ "data": 1.1,
43+ "valid": false
44+ }
45+ ]
46+ }
47+]
JSON-Schema-Test-Suite/tests/draft4/multipleOf.jsonnew file+60
@@ -0,0 +1,60 @@
1+[
2+ {
3+ "description": "by int",
4+ "schema": {"multipleOf": 2},
5+ "tests": [
6+ {
7+ "description": "int by int",
8+ "data": 10,
9+ "valid": true
10+ },
11+ {
12+ "description": "int by int fail",
13+ "data": 7,
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-numbers",
18+ "data": "foo",
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "by number",
25+ "schema": {"multipleOf": 1.5},
26+ "tests": [
27+ {
28+ "description": "zero is multiple of anything",
29+ "data": 0,
30+ "valid": true
31+ },
32+ {
33+ "description": "4.5 is multiple of 1.5",
34+ "data": 4.5,
35+ "valid": true
36+ },
37+ {
38+ "description": "35 is not multiple of 1.5",
39+ "data": 35,
40+ "valid": false
41+ }
42+ ]
43+ },
44+ {
45+ "description": "by small number",
46+ "schema": {"multipleOf": 0.0001},
47+ "tests": [
48+ {
49+ "description": "0.0075 is multiple of 0.0001",
50+ "data": 0.0075,
51+ "valid": true
52+ },
53+ {
54+ "description": "0.00751 is not multiple of 0.0001",
55+ "data": 0.00751,
56+ "valid": false
57+ }
58+ ]
59+ }
60+]
JSON-Schema-Test-Suite/tests/draft4/not.jsonnew file+96
@@ -0,0 +1,96 @@
1+[
2+ {
3+ "description": "not",
4+ "schema": {
5+ "not": {"type": "integer"}
6+ },
7+ "tests": [
8+ {
9+ "description": "allowed",
10+ "data": "foo",
11+ "valid": true
12+ },
13+ {
14+ "description": "disallowed",
15+ "data": 1,
16+ "valid": false
17+ }
18+ ]
19+ },
20+ {
21+ "description": "not multiple types",
22+ "schema": {
23+ "not": {"type": ["integer", "boolean"]}
24+ },
25+ "tests": [
26+ {
27+ "description": "valid",
28+ "data": "foo",
29+ "valid": true
30+ },
31+ {
32+ "description": "mismatch",
33+ "data": 1,
34+ "valid": false
35+ },
36+ {
37+ "description": "other mismatch",
38+ "data": true,
39+ "valid": false
40+ }
41+ ]
42+ },
43+ {
44+ "description": "not more complex schema",
45+ "schema": {
46+ "not": {
47+ "type": "object",
48+ "properties": {
49+ "foo": {
50+ "type": "string"
51+ }
52+ }
53+ }
54+ },
55+ "tests": [
56+ {
57+ "description": "match",
58+ "data": 1,
59+ "valid": true
60+ },
61+ {
62+ "description": "other match",
63+ "data": {"foo": 1},
64+ "valid": true
65+ },
66+ {
67+ "description": "mismatch",
68+ "data": {"foo": "bar"},
69+ "valid": false
70+ }
71+ ]
72+ },
73+ {
74+ "description": "forbidden property",
75+ "schema": {
76+ "properties": {
77+ "foo": {
78+ "not": {}
79+ }
80+ }
81+ },
82+ "tests": [
83+ {
84+ "description": "property present",
85+ "data": {"foo": 1, "bar": 2},
86+ "valid": false
87+ },
88+ {
89+ "description": "property absent",
90+ "data": {"bar": 1, "baz": 2},
91+ "valid": true
92+ }
93+ ]
94+ }
95+
96+]
JSON-Schema-Test-Suite/tests/draft4/oneOf.jsonnew file+68
@@ -0,0 +1,68 @@
1+[
2+ {
3+ "description": "oneOf",
4+ "schema": {
5+ "oneOf": [
6+ {
7+ "type": "integer"
8+ },
9+ {
10+ "minimum": 2
11+ }
12+ ]
13+ },
14+ "tests": [
15+ {
16+ "description": "first oneOf valid",
17+ "data": 1,
18+ "valid": true
19+ },
20+ {
21+ "description": "second oneOf valid",
22+ "data": 2.5,
23+ "valid": true
24+ },
25+ {
26+ "description": "both oneOf valid",
27+ "data": 3,
28+ "valid": false
29+ },
30+ {
31+ "description": "neither oneOf valid",
32+ "data": 1.5,
33+ "valid": false
34+ }
35+ ]
36+ },
37+ {
38+ "description": "oneOf with base schema",
39+ "schema": {
40+ "type": "string",
41+ "oneOf" : [
42+ {
43+ "minLength": 2
44+ },
45+ {
46+ "maxLength": 4
47+ }
48+ ]
49+ },
50+ "tests": [
51+ {
52+ "description": "mismatch base schema",
53+ "data": 3,
54+ "valid": false
55+ },
56+ {
57+ "description": "one oneOf valid",
58+ "data": "foobar",
59+ "valid": true
60+ },
61+ {
62+ "description": "both oneOf valid",
63+ "data": "foo",
64+ "valid": false
65+ }
66+ ]
67+ }
68+]
JSON-Schema-Test-Suite/tests/draft4/optional/bignum.jsonnew file+107
@@ -0,0 +1,107 @@
1+[
2+ {
3+ "description": "integer",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "a bignum is an integer",
8+ "data": 12345678910111213141516171819202122232425262728293031,
9+ "valid": true
10+ }
11+ ]
12+ },
13+ {
14+ "description": "number",
15+ "schema": {"type": "number"},
16+ "tests": [
17+ {
18+ "description": "a bignum is a number",
19+ "data": 98249283749234923498293171823948729348710298301928331,
20+ "valid": true
21+ }
22+ ]
23+ },
24+ {
25+ "description": "integer",
26+ "schema": {"type": "integer"},
27+ "tests": [
28+ {
29+ "description": "a negative bignum is an integer",
30+ "data": -12345678910111213141516171819202122232425262728293031,
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "number",
37+ "schema": {"type": "number"},
38+ "tests": [
39+ {
40+ "description": "a negative bignum is a number",
41+ "data": -98249283749234923498293171823948729348710298301928331,
42+ "valid": true
43+ }
44+ ]
45+ },
46+ {
47+ "description": "string",
48+ "schema": {"type": "string"},
49+ "tests": [
50+ {
51+ "description": "a bignum is not a string",
52+ "data": 98249283749234923498293171823948729348710298301928331,
53+ "valid": false
54+ }
55+ ]
56+ },
57+ {
58+ "description": "integer comparison",
59+ "schema": {"maximum": 18446744073709551615},
60+ "tests": [
61+ {
62+ "description": "comparison works for high numbers",
63+ "data": 18446744073709551600,
64+ "valid": true
65+ }
66+ ]
67+ },
68+ {
69+ "description": "float comparison with high precision",
70+ "schema": {
71+ "maximum": 972783798187987123879878123.18878137,
72+ "exclusiveMaximum": true
73+ },
74+ "tests": [
75+ {
76+ "description": "comparison works for high numbers",
77+ "data": 972783798187987123879878123.188781371,
78+ "valid": false
79+ }
80+ ]
81+ },
82+ {
83+ "description": "integer comparison",
84+ "schema": {"minimum": -18446744073709551615},
85+ "tests": [
86+ {
87+ "description": "comparison works for very negative numbers",
88+ "data": -18446744073709551600,
89+ "valid": true
90+ }
91+ ]
92+ },
93+ {
94+ "description": "float comparison with high precision on negative numbers",
95+ "schema": {
96+ "minimum": -972783798187987123879878123.18878137,
97+ "exclusiveMinimum": true
98+ },
99+ "tests": [
100+ {
101+ "description": "comparison works for very negative numbers",
102+ "data": -972783798187987123879878123.188781371,
103+ "valid": false
104+ }
105+ ]
106+ }
107+]
JSON-Schema-Test-Suite/tests/draft4/optional/ecmascript-regex.jsonnew file+13
@@ -0,0 +1,13 @@
1+[
2+ {
3+ "description": "ECMA 262 regex non-compliance",
4+ "schema": { "format": "regex" },
5+ "tests": [
6+ {
7+ "description": "ECMA 262 has no support for \\Z anchor from .NET",
8+ "data": "^\\S(|(.|\\n)*\\S)\\Z",
9+ "valid": false
10+ }
11+ ]
12+ }
13+]
JSON-Schema-Test-Suite/tests/draft4/optional/format.jsonnew file+148
@@ -0,0 +1,148 @@
1+[
2+ {
3+ "description": "validation of date-time strings",
4+ "schema": {"format": "date-time"},
5+ "tests": [
6+ {
7+ "description": "a valid date-time string",
8+ "data": "1963-06-19T08:30:06.283185Z",
9+ "valid": true
10+ },
11+ {
12+ "description": "an invalid date-time string",
13+ "data": "06/19/1963 08:30:06 PST",
14+ "valid": false
15+ },
16+ {
17+ "description": "only RFC3339 not all of ISO 8601 are valid",
18+ "data": "2013-350T01:01:01",
19+ "valid": false
20+ }
21+ ]
22+ },
23+ {
24+ "description": "validation of URIs",
25+ "schema": {"format": "uri"},
26+ "tests": [
27+ {
28+ "description": "a valid URI",
29+ "data": "http://foo.bar/?baz=qux#quux",
30+ "valid": true
31+ },
32+ {
33+ "description": "an invalid protocol-relative URI Reference",
34+ "data": "//foo.bar/?baz=qux#quux",
35+ "valid": false
36+ },
37+ {
38+ "description": "an invalid URI",
39+ "data": "\\\\WINDOWS\\fileshare",
40+ "valid": false
41+ },
42+ {
43+ "description": "an invalid URI though valid URI reference",
44+ "data": "abc",
45+ "valid": false
46+ }
47+ ]
48+ },
49+ {
50+ "description": "validation of e-mail addresses",
51+ "schema": {"format": "email"},
52+ "tests": [
53+ {
54+ "description": "a valid e-mail address",
55+ "data": "joe.bloggs@example.com",
56+ "valid": true
57+ },
58+ {
59+ "description": "an invalid e-mail address",
60+ "data": "2962",
61+ "valid": false
62+ }
63+ ]
64+ },
65+ {
66+ "description": "validation of IP addresses",
67+ "schema": {"format": "ipv4"},
68+ "tests": [
69+ {
70+ "description": "a valid IP address",
71+ "data": "192.168.0.1",
72+ "valid": true
73+ },
74+ {
75+ "description": "an IP address with too many components",
76+ "data": "127.0.0.0.1",
77+ "valid": false
78+ },
79+ {
80+ "description": "an IP address with out-of-range values",
81+ "data": "256.256.256.256",
82+ "valid": false
83+ },
84+ {
85+ "description": "an IP address without 4 components",
86+ "data": "127.0",
87+ "valid": false
88+ },
89+ {
90+ "description": "an IP address as an integer",
91+ "data": "0x7f000001",
92+ "valid": false
93+ }
94+ ]
95+ },
96+ {
97+ "description": "validation of IPv6 addresses",
98+ "schema": {"format": "ipv6"},
99+ "tests": [
100+ {
101+ "description": "a valid IPv6 address",
102+ "data": "::1",
103+ "valid": true
104+ },
105+ {
106+ "description": "an IPv6 address with out-of-range values",
107+ "data": "12345::",
108+ "valid": false
109+ },
110+ {
111+ "description": "an IPv6 address with too many components",
112+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
113+ "valid": false
114+ },
115+ {
116+ "description": "an IPv6 address containing illegal characters",
117+ "data": "::laptop",
118+ "valid": false
119+ }
120+ ]
121+ },
122+ {
123+ "description": "validation of host names",
124+ "schema": {"format": "hostname"},
125+ "tests": [
126+ {
127+ "description": "a valid host name",
128+ "data": "www.example.com",
129+ "valid": true
130+ },
131+ {
132+ "description": "a host name starting with an illegal character",
133+ "data": "-a-host-name-that-starts-with--",
134+ "valid": false
135+ },
136+ {
137+ "description": "a host name containing illegal characters",
138+ "data": "not_a_valid_host_name",
139+ "valid": false
140+ },
141+ {
142+ "description": "a host name with a component too long",
143+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
144+ "valid": false
145+ }
146+ ]
147+ }
148+]
JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.jsonnew file+15
@@ -0,0 +1,15 @@
1+[
2+ {
3+ "description": "some languages do not distinguish between different types of numeric value",
4+ "schema": {
5+ "type": "integer"
6+ },
7+ "tests": [
8+ {
9+ "description": "a float is not an integer even without fractional part",
10+ "data": 1.0,
11+ "valid": false
12+ }
13+ ]
14+ }
15+]
JSON-Schema-Test-Suite/tests/draft4/pattern.jsonnew file+34
@@ -0,0 +1,34 @@
1+[
2+ {
3+ "description": "pattern validation",
4+ "schema": {"pattern": "^a*$"},
5+ "tests": [
6+ {
7+ "description": "a matching pattern is valid",
8+ "data": "aaa",
9+ "valid": true
10+ },
11+ {
12+ "description": "a non-matching pattern is invalid",
13+ "data": "abc",
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-strings",
18+ "data": true,
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "pattern is not anchored",
25+ "schema": {"pattern": "a+"},
26+ "tests": [
27+ {
28+ "description": "matches a substring",
29+ "data": "xxaayy",
30+ "valid": true
31+ }
32+ ]
33+ }
34+]
JSON-Schema-Test-Suite/tests/draft4/patternProperties.jsonnew file+110
@@ -0,0 +1,110 @@
1+[
2+ {
3+ "description":
4+ "patternProperties validates properties matching a regex",
5+ "schema": {
6+ "patternProperties": {
7+ "f.*o": {"type": "integer"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "a single valid match is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "multiple valid matches is valid",
18+ "data": {"foo": 1, "foooooo" : 2},
19+ "valid": true
20+ },
21+ {
22+ "description": "a single invalid match is invalid",
23+ "data": {"foo": "bar", "fooooo": 2},
24+ "valid": false
25+ },
26+ {
27+ "description": "multiple invalid matches is invalid",
28+ "data": {"foo": "bar", "foooooo" : "baz"},
29+ "valid": false
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": 12,
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description": "multiple simultaneous patternProperties are validated",
40+ "schema": {
41+ "patternProperties": {
42+ "a*": {"type": "integer"},
43+ "aaa*": {"maximum": 20}
44+ }
45+ },
46+ "tests": [
47+ {
48+ "description": "a single valid match is valid",
49+ "data": {"a": 21},
50+ "valid": true
51+ },
52+ {
53+ "description": "a simultaneous match is valid",
54+ "data": {"aaaa": 18},
55+ "valid": true
56+ },
57+ {
58+ "description": "multiple matches is valid",
59+ "data": {"a": 21, "aaaa": 18},
60+ "valid": true
61+ },
62+ {
63+ "description": "an invalid due to one is invalid",
64+ "data": {"a": "bar"},
65+ "valid": false
66+ },
67+ {
68+ "description": "an invalid due to the other is invalid",
69+ "data": {"aaaa": 31},
70+ "valid": false
71+ },
72+ {
73+ "description": "an invalid due to both is invalid",
74+ "data": {"aaa": "foo", "aaaa": 31},
75+ "valid": false
76+ }
77+ ]
78+ },
79+ {
80+ "description": "regexes are not anchored by default and are case sensitive",
81+ "schema": {
82+ "patternProperties": {
83+ "[0-9]{2,}": { "type": "boolean" },
84+ "X_": { "type": "string" }
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "non recognized members are ignored",
90+ "data": { "answer 1": "42" },
91+ "valid": true
92+ },
93+ {
94+ "description": "recognized members are accounted for",
95+ "data": { "a31b": null },
96+ "valid": false
97+ },
98+ {
99+ "description": "regexes are case sensitive",
100+ "data": { "a_x_3": 3 },
101+ "valid": true
102+ },
103+ {
104+ "description": "regexes are case sensitive, 2",
105+ "data": { "a_X_3": 3 },
106+ "valid": false
107+ }
108+ ]
109+ }
110+]
JSON-Schema-Test-Suite/tests/draft4/properties.jsonnew file+92
@@ -0,0 +1,92 @@
1+[
2+ {
3+ "description": "object properties validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {"type": "integer"},
7+ "bar": {"type": "string"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "both properties present and valid is valid",
13+ "data": {"foo": 1, "bar": "baz"},
14+ "valid": true
15+ },
16+ {
17+ "description": "one property invalid is invalid",
18+ "data": {"foo": 1, "bar": {}},
19+ "valid": false
20+ },
21+ {
22+ "description": "both properties invalid is invalid",
23+ "data": {"foo": [], "bar": {}},
24+ "valid": false
25+ },
26+ {
27+ "description": "doesn't invalidate other properties",
28+ "data": {"quux": []},
29+ "valid": true
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": [],
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description":
40+ "properties, patternProperties, additionalProperties interaction",
41+ "schema": {
42+ "properties": {
43+ "foo": {"type": "array", "maxItems": 3},
44+ "bar": {"type": "array"}
45+ },
46+ "patternProperties": {"f.o": {"minItems": 2}},
47+ "additionalProperties": {"type": "integer"}
48+ },
49+ "tests": [
50+ {
51+ "description": "property validates property",
52+ "data": {"foo": [1, 2]},
53+ "valid": true
54+ },
55+ {
56+ "description": "property invalidates property",
57+ "data": {"foo": [1, 2, 3, 4]},
58+ "valid": false
59+ },
60+ {
61+ "description": "patternProperty invalidates property",
62+ "data": {"foo": []},
63+ "valid": false
64+ },
65+ {
66+ "description": "patternProperty validates nonproperty",
67+ "data": {"fxo": [1, 2]},
68+ "valid": true
69+ },
70+ {
71+ "description": "patternProperty invalidates nonproperty",
72+ "data": {"fxo": []},
73+ "valid": false
74+ },
75+ {
76+ "description": "additionalProperty ignores property",
77+ "data": {"bar": []},
78+ "valid": true
79+ },
80+ {
81+ "description": "additionalProperty validates others",
82+ "data": {"quux": 3},
83+ "valid": true
84+ },
85+ {
86+ "description": "additionalProperty invalidates others",
87+ "data": {"quux": "foo"},
88+ "valid": false
89+ }
90+ ]
91+ }
92+]
JSON-Schema-Test-Suite/tests/draft4/ref.jsonnew file+298
@@ -0,0 +1,298 @@
1+[
2+ {
3+ "description": "root pointer ref",
4+ "schema": {
5+ "properties": {
6+ "foo": {"$ref": "#"}
7+ },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "match",
13+ "data": {"foo": false},
14+ "valid": true
15+ },
16+ {
17+ "description": "recursive match",
18+ "data": {"foo": {"foo": false}},
19+ "valid": true
20+ },
21+ {
22+ "description": "mismatch",
23+ "data": {"bar": false},
24+ "valid": false
25+ },
26+ {
27+ "description": "recursive mismatch",
28+ "data": {"foo": {"bar": false}},
29+ "valid": false
30+ }
31+ ]
32+ },
33+ {
34+ "description": "relative pointer ref to object",
35+ "schema": {
36+ "properties": {
37+ "foo": {"type": "integer"},
38+ "bar": {"$ref": "#/properties/foo"}
39+ }
40+ },
41+ "tests": [
42+ {
43+ "description": "match",
44+ "data": {"bar": 3},
45+ "valid": true
46+ },
47+ {
48+ "description": "mismatch",
49+ "data": {"bar": true},
50+ "valid": false
51+ }
52+ ]
53+ },
54+ {
55+ "description": "relative pointer ref to array",
56+ "schema": {
57+ "items": [
58+ {"type": "integer"},
59+ {"$ref": "#/items/0"}
60+ ]
61+ },
62+ "tests": [
63+ {
64+ "description": "match array",
65+ "data": [1, 2],
66+ "valid": true
67+ },
68+ {
69+ "description": "mismatch array",
70+ "data": [1, "foo"],
71+ "valid": false
72+ }
73+ ]
74+ },
75+ {
76+ "description": "escaped pointer ref",
77+ "schema": {
78+ "tilda~field": {"type": "integer"},
79+ "slash/field": {"type": "integer"},
80+ "percent%field": {"type": "integer"},
81+ "properties": {
82+ "tilda": {"$ref": "#/tilda~0field"},
83+ "slash": {"$ref": "#/slash~1field"},
84+ "percent": {"$ref": "#/percent%25field"}
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "slash invalid",
90+ "data": {"slash": "aoeu"},
91+ "valid": false
92+ },
93+ {
94+ "description": "tilda invalid",
95+ "data": {"tilda": "aoeu"},
96+ "valid": false
97+ },
98+ {
99+ "description": "percent invalid",
100+ "data": {"percent": "aoeu"},
101+ "valid": false
102+ },
103+ {
104+ "description": "slash valid",
105+ "data": {"slash": 123},
106+ "valid": true
107+ },
108+ {
109+ "description": "tilda valid",
110+ "data": {"tilda": 123},
111+ "valid": true
112+ },
113+ {
114+ "description": "percent valid",
115+ "data": {"percent": 123},
116+ "valid": true
117+ }
118+ ]
119+ },
120+ {
121+ "description": "nested refs",
122+ "schema": {
123+ "definitions": {
124+ "a": {"type": "integer"},
125+ "b": {"$ref": "#/definitions/a"},
126+ "c": {"$ref": "#/definitions/b"}
127+ },
128+ "$ref": "#/definitions/c"
129+ },
130+ "tests": [
131+ {
132+ "description": "nested ref valid",
133+ "data": 5,
134+ "valid": true
135+ },
136+ {
137+ "description": "nested ref invalid",
138+ "data": "a",
139+ "valid": false
140+ }
141+ ]
142+ },
143+ {
144+ "description": "ref overrides any sibling keywords",
145+ "schema": {
146+ "definitions": {
147+ "reffed": {
148+ "type": "array"
149+ }
150+ },
151+ "properties": {
152+ "foo": {
153+ "$ref": "#/definitions/reffed",
154+ "maxItems": 2
155+ }
156+ }
157+ },
158+ "tests": [
159+ {
160+ "description": "ref valid",
161+ "data": { "foo": [] },
162+ "valid": true
163+ },
164+ {
165+ "description": "ref valid, maxItems ignored",
166+ "data": { "foo": [ 1, 2, 3] },
167+ "valid": true
168+ },
169+ {
170+ "description": "ref invalid",
171+ "data": { "foo": "string" },
172+ "valid": false
173+ }
174+ ]
175+ },
176+ {
177+ "description": "remote ref, containing refs itself",
178+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
179+ "tests": [
180+ {
181+ "description": "remote ref valid",
182+ "data": {"minLength": 1},
183+ "valid": true
184+ },
185+ {
186+ "description": "remote ref invalid",
187+ "data": {"minLength": -1},
188+ "valid": false
189+ }
190+ ]
191+ },
192+ {
193+ "description": "property named $ref that is not a reference",
194+ "schema": {
195+ "properties": {
196+ "$ref": {"type": "string"}
197+ }
198+ },
199+ "tests": [
200+ {
201+ "description": "property named $ref valid",
202+ "data": {"$ref": "a"},
203+ "valid": true
204+ },
205+ {
206+ "description": "property named $ref invalid",
207+ "data": {"$ref": 2},
208+ "valid": false
209+ }
210+ ]
211+ },
212+ {
213+ "description": "Recursive references between schemas",
214+ "schema": {
215+ "description": "tree of nodes",
216+ "type": "object",
217+ "properties": {
218+ "meta": {"type": "string"},
219+ "nodes": {
220+ "type": "array",
221+ "items": {"$ref": "#/definitions/node"}
222+ }
223+ },
224+ "required": ["meta", "nodes"],
225+ "definitions": {
226+ "node": {
227+ "description": "node",
228+ "type": "object",
229+ "properties": {
230+ "value": {"type": "number"},
231+ "subtree": {"$ref": "#"}
232+ },
233+ "required": ["value"]
234+ }
235+ }
236+ },
237+ "tests": [
238+ {
239+ "description": "valid tree",
240+ "data": {
241+ "meta": "root",
242+ "nodes": [
243+ {
244+ "value": 1,
245+ "subtree": {
246+ "meta": "child",
247+ "nodes": [
248+ {"value": 1.1},
249+ {"value": 1.2}
250+ ]
251+ }
252+ },
253+ {
254+ "value": 2,
255+ "subtree": {
256+ "meta": "child",
257+ "nodes": [
258+ {"value": 2.1},
259+ {"value": 2.2}
260+ ]
261+ }
262+ }
263+ ]
264+ },
265+ "valid": true
266+ },
267+ {
268+ "description": "invalid tree",
269+ "data": {
270+ "meta": "root",
271+ "nodes": [
272+ {
273+ "value": 1,
274+ "subtree": {
275+ "meta": "child",
276+ "nodes": [
277+ {"value": "string is invalid"},
278+ {"value": 1.2}
279+ ]
280+ }
281+ },
282+ {
283+ "value": 2,
284+ "subtree": {
285+ "meta": "child",
286+ "nodes": [
287+ {"value": 2.1},
288+ {"value": 2.2}
289+ ]
290+ }
291+ }
292+ ]
293+ },
294+ "valid": false
295+ }
296+ ]
297+ }
298+]
JSON-Schema-Test-Suite/tests/draft4/refRemote.jsonnew file+171
@@ -0,0 +1,171 @@
1+[
2+ {
3+ "description": "remote ref",
4+ "schema": {"$ref": "http://localhost:1234/integer.json"},
5+ "tests": [
6+ {
7+ "description": "remote ref valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "remote ref invalid",
13+ "data": "a",
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "fragment within remote ref",
20+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
21+ "tests": [
22+ {
23+ "description": "remote fragment valid",
24+ "data": 1,
25+ "valid": true
26+ },
27+ {
28+ "description": "remote fragment invalid",
29+ "data": "a",
30+ "valid": false
31+ }
32+ ]
33+ },
34+ {
35+ "description": "ref within remote ref",
36+ "schema": {
37+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
38+ },
39+ "tests": [
40+ {
41+ "description": "ref within ref valid",
42+ "data": 1,
43+ "valid": true
44+ },
45+ {
46+ "description": "ref within ref invalid",
47+ "data": "a",
48+ "valid": false
49+ }
50+ ]
51+ },
52+ {
53+ "description": "base URI change",
54+ "schema": {
55+ "id": "http://localhost:1234/",
56+ "items": {
57+ "id": "folder/",
58+ "items": {"$ref": "folderInteger.json"}
59+ }
60+ },
61+ "tests": [
62+ {
63+ "description": "base URI change ref valid",
64+ "data": [[1]],
65+ "valid": true
66+ },
67+ {
68+ "description": "base URI change ref invalid",
69+ "data": [["a"]],
70+ "valid": false
71+ }
72+ ]
73+ },
74+ {
75+ "description": "base URI change - change folder",
76+ "schema": {
77+ "id": "http://localhost:1234/scope_change_defs1.json",
78+ "type" : "object",
79+ "properties": {
80+ "list": {"$ref": "#/definitions/baz"}
81+ },
82+ "definitions": {
83+ "baz": {
84+ "id": "folder/",
85+ "type": "array",
86+ "items": {"$ref": "folderInteger.json"}
87+ }
88+ }
89+ },
90+ "tests": [
91+ {
92+ "description": "number is valid",
93+ "data": {"list": [1]},
94+ "valid": true
95+ },
96+ {
97+ "description": "string is invalid",
98+ "data": {"list": ["a"]},
99+ "valid": false
100+ }
101+ ]
102+ },
103+ {
104+ "description": "base URI change - change folder in subschema",
105+ "schema": {
106+ "id": "http://localhost:1234/scope_change_defs2.json",
107+ "type" : "object",
108+ "properties": {
109+ "list": {"$ref": "#/definitions/baz/definitions/bar"}
110+ },
111+ "definitions": {
112+ "baz": {
113+ "id": "folder/",
114+ "definitions": {
115+ "bar": {
116+ "type": "array",
117+ "items": {"$ref": "folderInteger.json"}
118+ }
119+ }
120+ }
121+ }
122+ },
123+ "tests": [
124+ {
125+ "description": "number is valid",
126+ "data": {"list": [1]},
127+ "valid": true
128+ },
129+ {
130+ "description": "string is invalid",
131+ "data": {"list": ["a"]},
132+ "valid": false
133+ }
134+ ]
135+ },
136+ {
137+ "description": "root ref in remote ref",
138+ "schema": {
139+ "id": "http://localhost:1234/object",
140+ "type": "object",
141+ "properties": {
142+ "name": {"$ref": "name.json#/definitions/orNull"}
143+ }
144+ },
145+ "tests": [
146+ {
147+ "description": "string is valid",
148+ "data": {
149+ "name": "foo"
150+ },
151+ "valid": true
152+ },
153+ {
154+ "description": "null is valid",
155+ "data": {
156+ "name": null
157+ },
158+ "valid": true
159+ },
160+ {
161+ "description": "object is invalid",
162+ "data": {
163+ "name": {
164+ "name": null
165+ }
166+ },
167+ "valid": false
168+ }
169+ ]
170+ }
171+]
JSON-Schema-Test-Suite/tests/draft4/required.jsonnew file+44
@@ -0,0 +1,44 @@
1+[
2+ {
3+ "description": "required validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {},
7+ "bar": {}
8+ },
9+ "required": ["foo"]
10+ },
11+ "tests": [
12+ {
13+ "description": "present required property is valid",
14+ "data": {"foo": 1},
15+ "valid": true
16+ },
17+ {
18+ "description": "non-present required property is invalid",
19+ "data": {"bar": 1},
20+ "valid": false
21+ },
22+ {
23+ "description": "ignores non-objects",
24+ "data": 12,
25+ "valid": true
26+ }
27+ ]
28+ },
29+ {
30+ "description": "required default validation",
31+ "schema": {
32+ "properties": {
33+ "foo": {}
34+ }
35+ },
36+ "tests": [
37+ {
38+ "description": "not required by default",
39+ "data": {},
40+ "valid": true
41+ }
42+ ]
43+ }
44+]
JSON-Schema-Test-Suite/tests/draft4/type.jsonnew file+345
@@ -0,0 +1,345 @@
1+[
2+ {
3+ "description": "integer type matches integers",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "an integer is an integer",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "a float is not an integer",
13+ "data": 1.1,
14+ "valid": false
15+ },
16+ {
17+ "description": "a string is not an integer",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "a string is still not an integer, even if it looks like one",
23+ "data": "1",
24+ "valid": false
25+ },
26+ {
27+ "description": "an object is not an integer",
28+ "data": {},
29+ "valid": false
30+ },
31+ {
32+ "description": "an array is not an integer",
33+ "data": [],
34+ "valid": false
35+ },
36+ {
37+ "description": "a boolean is not an integer",
38+ "data": true,
39+ "valid": false
40+ },
41+ {
42+ "description": "null is not an integer",
43+ "data": null,
44+ "valid": false
45+ }
46+ ]
47+ },
48+ {
49+ "description": "number type matches numbers",
50+ "schema": {"type": "number"},
51+ "tests": [
52+ {
53+ "description": "an integer is a number",
54+ "data": 1,
55+ "valid": true
56+ },
57+ {
58+ "description": "a float is a number",
59+ "data": 1.1,
60+ "valid": true
61+ },
62+ {
63+ "description": "a string is not a number",
64+ "data": "foo",
65+ "valid": false
66+ },
67+ {
68+ "description": "a string is still not a number, even if it looks like one",
69+ "data": "1",
70+ "valid": false
71+ },
72+ {
73+ "description": "an object is not a number",
74+ "data": {},
75+ "valid": false
76+ },
77+ {
78+ "description": "an array is not a number",
79+ "data": [],
80+ "valid": false
81+ },
82+ {
83+ "description": "a boolean is not a number",
84+ "data": true,
85+ "valid": false
86+ },
87+ {
88+ "description": "null is not a number",
89+ "data": null,
90+ "valid": false
91+ }
92+ ]
93+ },
94+ {
95+ "description": "string type matches strings",
96+ "schema": {"type": "string"},
97+ "tests": [
98+ {
99+ "description": "1 is not a string",
100+ "data": 1,
101+ "valid": false
102+ },
103+ {
104+ "description": "a float is not a string",
105+ "data": 1.1,
106+ "valid": false
107+ },
108+ {
109+ "description": "a string is a string",
110+ "data": "foo",
111+ "valid": true
112+ },
113+ {
114+ "description": "a string is still a string, even if it looks like a number",
115+ "data": "1",
116+ "valid": true
117+ },
118+ {
119+ "description": "an object is not a string",
120+ "data": {},
121+ "valid": false
122+ },
123+ {
124+ "description": "an array is not a string",
125+ "data": [],
126+ "valid": false
127+ },
128+ {
129+ "description": "a boolean is not a string",
130+ "data": true,
131+ "valid": false
132+ },
133+ {
134+ "description": "null is not a string",
135+ "data": null,
136+ "valid": false
137+ }
138+ ]
139+ },
140+ {
141+ "description": "object type matches objects",
142+ "schema": {"type": "object"},
143+ "tests": [
144+ {
145+ "description": "an integer is not an object",
146+ "data": 1,
147+ "valid": false
148+ },
149+ {
150+ "description": "a float is not an object",
151+ "data": 1.1,
152+ "valid": false
153+ },
154+ {
155+ "description": "a string is not an object",
156+ "data": "foo",
157+ "valid": false
158+ },
159+ {
160+ "description": "an object is an object",
161+ "data": {},
162+ "valid": true
163+ },
164+ {
165+ "description": "an array is not an object",
166+ "data": [],
167+ "valid": false
168+ },
169+ {
170+ "description": "a boolean is not an object",
171+ "data": true,
172+ "valid": false
173+ },
174+ {
175+ "description": "null is not an object",
176+ "data": null,
177+ "valid": false
178+ }
179+ ]
180+ },
181+ {
182+ "description": "array type matches arrays",
183+ "schema": {"type": "array"},
184+ "tests": [
185+ {
186+ "description": "an integer is not an array",
187+ "data": 1,
188+ "valid": false
189+ },
190+ {
191+ "description": "a float is not an array",
192+ "data": 1.1,
193+ "valid": false
194+ },
195+ {
196+ "description": "a string is not an array",
197+ "data": "foo",
198+ "valid": false
199+ },
200+ {
201+ "description": "an object is not an array",
202+ "data": {},
203+ "valid": false
204+ },
205+ {
206+ "description": "an array is an array",
207+ "data": [],
208+ "valid": true
209+ },
210+ {
211+ "description": "a boolean is not an array",
212+ "data": true,
213+ "valid": false
214+ },
215+ {
216+ "description": "null is not an array",
217+ "data": null,
218+ "valid": false
219+ }
220+ ]
221+ },
222+ {
223+ "description": "boolean type matches booleans",
224+ "schema": {"type": "boolean"},
225+ "tests": [
226+ {
227+ "description": "an integer is not a boolean",
228+ "data": 1,
229+ "valid": false
230+ },
231+ {
232+ "description": "a float is not a boolean",
233+ "data": 1.1,
234+ "valid": false
235+ },
236+ {
237+ "description": "a string is not a boolean",
238+ "data": "foo",
239+ "valid": false
240+ },
241+ {
242+ "description": "an object is not a boolean",
243+ "data": {},
244+ "valid": false
245+ },
246+ {
247+ "description": "an array is not a boolean",
248+ "data": [],
249+ "valid": false
250+ },
251+ {
252+ "description": "a boolean is a boolean",
253+ "data": true,
254+ "valid": true
255+ },
256+ {
257+ "description": "null is not a boolean",
258+ "data": null,
259+ "valid": false
260+ }
261+ ]
262+ },
263+ {
264+ "description": "null type matches only the null object",
265+ "schema": {"type": "null"},
266+ "tests": [
267+ {
268+ "description": "an integer is not null",
269+ "data": 1,
270+ "valid": false
271+ },
272+ {
273+ "description": "a float is not null",
274+ "data": 1.1,
275+ "valid": false
276+ },
277+ {
278+ "description": "a string is not null",
279+ "data": "foo",
280+ "valid": false
281+ },
282+ {
283+ "description": "an object is not null",
284+ "data": {},
285+ "valid": false
286+ },
287+ {
288+ "description": "an array is not null",
289+ "data": [],
290+ "valid": false
291+ },
292+ {
293+ "description": "a boolean is not null",
294+ "data": true,
295+ "valid": false
296+ },
297+ {
298+ "description": "null is null",
299+ "data": null,
300+ "valid": true
301+ }
302+ ]
303+ },
304+ {
305+ "description": "multiple types can be specified in an array",
306+ "schema": {"type": ["integer", "string"]},
307+ "tests": [
308+ {
309+ "description": "an integer is valid",
310+ "data": 1,
311+ "valid": true
312+ },
313+ {
314+ "description": "a string is valid",
315+ "data": "foo",
316+ "valid": true
317+ },
318+ {
319+ "description": "a float is invalid",
320+ "data": 1.1,
321+ "valid": false
322+ },
323+ {
324+ "description": "an object is invalid",
325+ "data": {},
326+ "valid": false
327+ },
328+ {
329+ "description": "an array is invalid",
330+ "data": [],
331+ "valid": false
332+ },
333+ {
334+ "description": "a boolean is invalid",
335+ "data": true,
336+ "valid": false
337+ },
338+ {
339+ "description": "null is invalid",
340+ "data": null,
341+ "valid": false
342+ }
343+ ]
344+ }
345+]
JSON-Schema-Test-Suite/tests/draft4/uniqueItems.jsonnew file+79
@@ -0,0 +1,79 @@
1+[
2+ {
3+ "description": "uniqueItems validation",
4+ "schema": {"uniqueItems": true},
5+ "tests": [
6+ {
7+ "description": "unique array of integers is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "non-unique array of integers is invalid",
13+ "data": [1, 1],
14+ "valid": false
15+ },
16+ {
17+ "description": "numbers are unique if mathematically unequal",
18+ "data": [1.0, 1.00, 1],
19+ "valid": false
20+ },
21+ {
22+ "description": "unique array of objects is valid",
23+ "data": [{"foo": "bar"}, {"foo": "baz"}],
24+ "valid": true
25+ },
26+ {
27+ "description": "non-unique array of objects is invalid",
28+ "data": [{"foo": "bar"}, {"foo": "bar"}],
29+ "valid": false
30+ },
31+ {
32+ "description": "unique array of nested objects is valid",
33+ "data": [
34+ {"foo": {"bar" : {"baz" : true}}},
35+ {"foo": {"bar" : {"baz" : false}}}
36+ ],
37+ "valid": true
38+ },
39+ {
40+ "description": "non-unique array of nested objects is invalid",
41+ "data": [
42+ {"foo": {"bar" : {"baz" : true}}},
43+ {"foo": {"bar" : {"baz" : true}}}
44+ ],
45+ "valid": false
46+ },
47+ {
48+ "description": "unique array of arrays is valid",
49+ "data": [["foo"], ["bar"]],
50+ "valid": true
51+ },
52+ {
53+ "description": "non-unique array of arrays is invalid",
54+ "data": [["foo"], ["foo"]],
55+ "valid": false
56+ },
57+ {
58+ "description": "1 and true are unique",
59+ "data": [1, true],
60+ "valid": true
61+ },
62+ {
63+ "description": "0 and false are unique",
64+ "data": [0, false],
65+ "valid": true
66+ },
67+ {
68+ "description": "unique heterogeneous types are valid",
69+ "data": [{}, [1], true, null, 1],
70+ "valid": true
71+ },
72+ {
73+ "description": "non-unique heterogeneous types are invalid",
74+ "data": [{}, [1], true, null, {}, 1],
75+ "valid": false
76+ }
77+ ]
78+ }
79+]
JSON-Schema-Test-Suite/tests/draft6/additionalItems.jsonnew file+87
@@ -0,0 +1,87 @@
1+[
2+ {
3+ "description": "additionalItems as schema",
4+ "schema": {
5+ "items": [{}],
6+ "additionalItems": {"type": "integer"}
7+ },
8+ "tests": [
9+ {
10+ "description": "additional items match schema",
11+ "data": [ null, 2, 3, 4 ],
12+ "valid": true
13+ },
14+ {
15+ "description": "additional items do not match schema",
16+ "data": [ null, 2, 3, "foo" ],
17+ "valid": false
18+ }
19+ ]
20+ },
21+ {
22+ "description": "items is schema, no additionalItems",
23+ "schema": {
24+ "items": {},
25+ "additionalItems": false
26+ },
27+ "tests": [
28+ {
29+ "description": "all items match schema",
30+ "data": [ 1, 2, 3, 4, 5 ],
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "array of items with no additionalItems",
37+ "schema": {
38+ "items": [{}, {}, {}],
39+ "additionalItems": false
40+ },
41+ "tests": [
42+ {
43+ "description": "fewer number of items present",
44+ "data": [ 1, 2 ],
45+ "valid": true
46+ },
47+ {
48+ "description": "equal number of items present",
49+ "data": [ 1, 2, 3 ],
50+ "valid": true
51+ },
52+ {
53+ "description": "additional items are not permitted",
54+ "data": [ 1, 2, 3, 4 ],
55+ "valid": false
56+ }
57+ ]
58+ },
59+ {
60+ "description": "additionalItems as false without items",
61+ "schema": {"additionalItems": false},
62+ "tests": [
63+ {
64+ "description":
65+ "items defaults to empty schema so everything is valid",
66+ "data": [ 1, 2, 3, 4, 5 ],
67+ "valid": true
68+ },
69+ {
70+ "description": "ignores non-arrays",
71+ "data": {"foo" : "bar"},
72+ "valid": true
73+ }
74+ ]
75+ },
76+ {
77+ "description": "additionalItems are allowed by default",
78+ "schema": {"items": [{"type": "integer"}]},
79+ "tests": [
80+ {
81+ "description": "only the first item is validated",
82+ "data": [1, "foo", false],
83+ "valid": true
84+ }
85+ ]
86+ }
87+]
JSON-Schema-Test-Suite/tests/draft6/additionalProperties.jsonnew file+88
@@ -0,0 +1,88 @@
1+[
2+ {
3+ "description":
4+ "additionalProperties being false does not allow other properties",
5+ "schema": {
6+ "properties": {"foo": {}, "bar": {}},
7+ "patternProperties": { "^v": {} },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "no additional properties is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "an additional property is invalid",
18+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": [1, 2, 3],
24+ "valid": true
25+ },
26+ {
27+ "description": "patternProperties are not additional properties",
28+ "data": {"foo":1, "vroom": 2},
29+ "valid": true
30+ }
31+ ]
32+ },
33+ {
34+ "description":
35+ "additionalProperties allows a schema which should validate",
36+ "schema": {
37+ "properties": {"foo": {}, "bar": {}},
38+ "additionalProperties": {"type": "boolean"}
39+ },
40+ "tests": [
41+ {
42+ "description": "no additional properties is valid",
43+ "data": {"foo": 1},
44+ "valid": true
45+ },
46+ {
47+ "description": "an additional valid property is valid",
48+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
49+ "valid": true
50+ },
51+ {
52+ "description": "an additional invalid property is invalid",
53+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
54+ "valid": false
55+ }
56+ ]
57+ },
58+ {
59+ "description":
60+ "additionalProperties can exist by itself",
61+ "schema": {
62+ "additionalProperties": {"type": "boolean"}
63+ },
64+ "tests": [
65+ {
66+ "description": "an additional valid property is valid",
67+ "data": {"foo" : true},
68+ "valid": true
69+ },
70+ {
71+ "description": "an additional invalid property is invalid",
72+ "data": {"foo" : 1},
73+ "valid": false
74+ }
75+ ]
76+ },
77+ {
78+ "description": "additionalProperties are allowed by default",
79+ "schema": {"properties": {"foo": {}, "bar": {}}},
80+ "tests": [
81+ {
82+ "description": "additional properties are allowed",
83+ "data": {"foo": 1, "bar": 2, "quux": true},
84+ "valid": true
85+ }
86+ ]
87+ }
88+]
JSON-Schema-Test-Suite/tests/draft6/allOf.jsonnew file+145
@@ -0,0 +1,145 @@
1+[
2+ {
3+ "description": "allOf",
4+ "schema": {
5+ "allOf": [
6+ {
7+ "properties": {
8+ "bar": {"type": "integer"}
9+ },
10+ "required": ["bar"]
11+ },
12+ {
13+ "properties": {
14+ "foo": {"type": "string"}
15+ },
16+ "required": ["foo"]
17+ }
18+ ]
19+ },
20+ "tests": [
21+ {
22+ "description": "allOf",
23+ "data": {"foo": "baz", "bar": 2},
24+ "valid": true
25+ },
26+ {
27+ "description": "mismatch second",
28+ "data": {"foo": "baz"},
29+ "valid": false
30+ },
31+ {
32+ "description": "mismatch first",
33+ "data": {"bar": 2},
34+ "valid": false
35+ },
36+ {
37+ "description": "wrong type",
38+ "data": {"foo": "baz", "bar": "quux"},
39+ "valid": false
40+ }
41+ ]
42+ },
43+ {
44+ "description": "allOf with base schema",
45+ "schema": {
46+ "properties": {"bar": {"type": "integer"}},
47+ "required": ["bar"],
48+ "allOf" : [
49+ {
50+ "properties": {
51+ "foo": {"type": "string"}
52+ },
53+ "required": ["foo"]
54+ },
55+ {
56+ "properties": {
57+ "baz": {"type": "null"}
58+ },
59+ "required": ["baz"]
60+ }
61+ ]
62+ },
63+ "tests": [
64+ {
65+ "description": "valid",
66+ "data": {"foo": "quux", "bar": 2, "baz": null},
67+ "valid": true
68+ },
69+ {
70+ "description": "mismatch base schema",
71+ "data": {"foo": "quux", "baz": null},
72+ "valid": false
73+ },
74+ {
75+ "description": "mismatch first allOf",
76+ "data": {"bar": 2, "baz": null},
77+ "valid": false
78+ },
79+ {
80+ "description": "mismatch second allOf",
81+ "data": {"foo": "quux", "bar": 2},
82+ "valid": false
83+ },
84+ {
85+ "description": "mismatch both",
86+ "data": {"bar": 2},
87+ "valid": false
88+ }
89+ ]
90+ },
91+ {
92+ "description": "allOf simple types",
93+ "schema": {
94+ "allOf": [
95+ {"maximum": 30},
96+ {"minimum": 20}
97+ ]
98+ },
99+ "tests": [
100+ {
101+ "description": "valid",
102+ "data": 25,
103+ "valid": true
104+ },
105+ {
106+ "description": "mismatch one",
107+ "data": 35,
108+ "valid": false
109+ }
110+ ]
111+ },
112+ {
113+ "description": "allOf with boolean schemas, all true",
114+ "schema": {"allOf": [true, true]},
115+ "tests": [
116+ {
117+ "description": "any value is valid",
118+ "data": "foo",
119+ "valid": true
120+ }
121+ ]
122+ },
123+ {
124+ "description": "allOf with boolean schemas, some false",
125+ "schema": {"allOf": [true, false]},
126+ "tests": [
127+ {
128+ "description": "any value is invalid",
129+ "data": "foo",
130+ "valid": false
131+ }
132+ ]
133+ },
134+ {
135+ "description": "allOf with boolean schemas, all false",
136+ "schema": {"allOf": [false, false]},
137+ "tests": [
138+ {
139+ "description": "any value is invalid",
140+ "data": "foo",
141+ "valid": false
142+ }
143+ ]
144+ }
145+]
JSON-Schema-Test-Suite/tests/draft6/anyOf.jsonnew file+101
@@ -0,0 +1,101 @@
1+[
2+ {
3+ "description": "anyOf",
4+ "schema": {
5+ "anyOf": [
6+ {
7+ "type": "integer"
8+ },
9+ {
10+ "minimum": 2
11+ }
12+ ]
13+ },
14+ "tests": [
15+ {
16+ "description": "first anyOf valid",
17+ "data": 1,
18+ "valid": true
19+ },
20+ {
21+ "description": "second anyOf valid",
22+ "data": 2.5,
23+ "valid": true
24+ },
25+ {
26+ "description": "both anyOf valid",
27+ "data": 3,
28+ "valid": true
29+ },
30+ {
31+ "description": "neither anyOf valid",
32+ "data": 1.5,
33+ "valid": false
34+ }
35+ ]
36+ },
37+ {
38+ "description": "anyOf with base schema",
39+ "schema": {
40+ "type": "string",
41+ "anyOf" : [
42+ {
43+ "maxLength": 2
44+ },
45+ {
46+ "minLength": 4
47+ }
48+ ]
49+ },
50+ "tests": [
51+ {
52+ "description": "mismatch base schema",
53+ "data": 3,
54+ "valid": false
55+ },
56+ {
57+ "description": "one anyOf valid",
58+ "data": "foobar",
59+ "valid": true
60+ },
61+ {
62+ "description": "both anyOf invalid",
63+ "data": "foo",
64+ "valid": false
65+ }
66+ ]
67+ },
68+ {
69+ "description": "anyOf with boolean schemas, all true",
70+ "schema": {"anyOf": [true, true]},
71+ "tests": [
72+ {
73+ "description": "any value is valid",
74+ "data": "foo",
75+ "valid": true
76+ }
77+ ]
78+ },
79+ {
80+ "description": "anyOf with boolean schemas, some true",
81+ "schema": {"anyOf": [true, false]},
82+ "tests": [
83+ {
84+ "description": "any value is valid",
85+ "data": "foo",
86+ "valid": true
87+ }
88+ ]
89+ },
90+ {
91+ "description": "anyOf with boolean schemas, all false",
92+ "schema": {"anyOf": [false, false]},
93+ "tests": [
94+ {
95+ "description": "any value is invalid",
96+ "data": "foo",
97+ "valid": false
98+ }
99+ ]
100+ }
101+]
JSON-Schema-Test-Suite/tests/draft6/boolean_schema.jsonnew file+104
@@ -0,0 +1,104 @@
1+[
2+ {
3+ "description": "boolean schema 'true'",
4+ "schema": true,
5+ "tests": [
6+ {
7+ "description": "number is valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "string is valid",
13+ "data": "foo",
14+ "valid": true
15+ },
16+ {
17+ "description": "boolean true is valid",
18+ "data": true,
19+ "valid": true
20+ },
21+ {
22+ "description": "boolean false is valid",
23+ "data": false,
24+ "valid": true
25+ },
26+ {
27+ "description": "null is valid",
28+ "data": null,
29+ "valid": true
30+ },
31+ {
32+ "description": "object is valid",
33+ "data": {"foo": "bar"},
34+ "valid": true
35+ },
36+ {
37+ "description": "empty object is valid",
38+ "data": {},
39+ "valid": true
40+ },
41+ {
42+ "description": "array is valid",
43+ "data": ["foo"],
44+ "valid": true
45+ },
46+ {
47+ "description": "empty array is valid",
48+ "data": [],
49+ "valid": true
50+ }
51+ ]
52+ },
53+ {
54+ "description": "boolean schema 'false'",
55+ "schema": false,
56+ "tests": [
57+ {
58+ "description": "number is invalid",
59+ "data": 1,
60+ "valid": false
61+ },
62+ {
63+ "description": "string is invalid",
64+ "data": "foo",
65+ "valid": false
66+ },
67+ {
68+ "description": "boolean true is invalid",
69+ "data": true,
70+ "valid": false
71+ },
72+ {
73+ "description": "boolean false is invalid",
74+ "data": false,
75+ "valid": false
76+ },
77+ {
78+ "description": "null is invalid",
79+ "data": null,
80+ "valid": false
81+ },
82+ {
83+ "description": "object is invalid",
84+ "data": {"foo": "bar"},
85+ "valid": false
86+ },
87+ {
88+ "description": "empty object is invalid",
89+ "data": {},
90+ "valid": false
91+ },
92+ {
93+ "description": "array is invalid",
94+ "data": ["foo"],
95+ "valid": false
96+ },
97+ {
98+ "description": "empty array is invalid",
99+ "data": [],
100+ "valid": false
101+ }
102+ ]
103+ }
104+]
JSON-Schema-Test-Suite/tests/draft6/const.jsonnew file+65
@@ -0,0 +1,65 @@
1+[
2+ {
3+ "description": "const validation",
4+ "schema": {"const": 2},
5+ "tests": [
6+ {
7+ "description": "same value is valid",
8+ "data": 2,
9+ "valid": true
10+ },
11+ {
12+ "description": "another value is invalid",
13+ "data": 5,
14+ "valid": false
15+ },
16+ {
17+ "description": "another type is invalid",
18+ "data": "a",
19+ "valid": false
20+ }
21+ ]
22+ },
23+ {
24+ "description": "const with object",
25+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
26+ "tests": [
27+ {
28+ "description": "same object is valid",
29+ "data": {"foo": "bar", "baz": "bax"},
30+ "valid": true
31+ },
32+ {
33+ "description": "same object with different property order is valid",
34+ "data": {"baz": "bax", "foo": "bar"},
35+ "valid": true
36+ },
37+ {
38+ "description": "another object is invalid",
39+ "data": {"foo": "bar"},
40+ "valid": false
41+ },
42+ {
43+ "description": "another type is invalid",
44+ "data": [1, 2],
45+ "valid": false
46+ }
47+ ]
48+ },
49+ {
50+ "description": "const with null",
51+ "schema": {"const": null},
52+ "tests": [
53+ {
54+ "description": "null is valid",
55+ "data": null,
56+ "valid": true
57+ },
58+ {
59+ "description": "not null is invalid",
60+ "data": 0,
61+ "valid": false
62+ }
63+ ]
64+ }
65+]
JSON-Schema-Test-Suite/tests/draft6/contains.jsonnew file+95
@@ -0,0 +1,95 @@
1+[
2+ {
3+ "description": "contains keyword validation",
4+ "schema": {
5+ "contains": {"minimum": 5}
6+ },
7+ "tests": [
8+ {
9+ "description": "array with item matching schema (5) is valid",
10+ "data": [3, 4, 5],
11+ "valid": true
12+ },
13+ {
14+ "description": "array with item matching schema (6) is valid",
15+ "data": [3, 4, 6],
16+ "valid": true
17+ },
18+ {
19+ "description": "array with two items matching schema (5, 6) is valid",
20+ "data": [3, 4, 5, 6],
21+ "valid": true
22+ },
23+ {
24+ "description": "array without items matching schema is invalid",
25+ "data": [2, 3, 4],
26+ "valid": false
27+ },
28+ {
29+ "description": "empty array is invalid",
30+ "data": [],
31+ "valid": false
32+ },
33+ {
34+ "description": "not array is valid",
35+ "data": {},
36+ "valid": true
37+ }
38+ ]
39+ },
40+ {
41+ "description": "contains keyword with const keyword",
42+ "schema": {
43+ "contains": { "const": 5 }
44+ },
45+ "tests": [
46+ {
47+ "description": "array with item 5 is valid",
48+ "data": [3, 4, 5],
49+ "valid": true
50+ },
51+ {
52+ "description": "array with two items 5 is valid",
53+ "data": [3, 4, 5, 5],
54+ "valid": true
55+ },
56+ {
57+ "description": "array without item 5 is invalid",
58+ "data": [1, 2, 3, 4],
59+ "valid": false
60+ }
61+ ]
62+ },
63+ {
64+ "description": "contains keyword with boolean schema true",
65+ "schema": {"contains": true},
66+ "tests": [
67+ {
68+ "description": "any non-empty array is valid",
69+ "data": ["foo"],
70+ "valid": true
71+ },
72+ {
73+ "description": "empty array is invalid",
74+ "data": [],
75+ "valid": false
76+ }
77+ ]
78+ },
79+ {
80+ "description": "contains keyword with boolean schema false",
81+ "schema": {"contains": false},
82+ "tests": [
83+ {
84+ "description": "any non-empty array is invalid",
85+ "data": ["foo"],
86+ "valid": false
87+ },
88+ {
89+ "description": "empty array is invalid",
90+ "data": [],
91+ "valid": false
92+ }
93+ ]
94+ }
95+]
JSON-Schema-Test-Suite/tests/draft6/default.jsonnew file+49
@@ -0,0 +1,49 @@
1+[
2+ {
3+ "description": "invalid type for default",
4+ "schema": {
5+ "properties": {
6+ "foo": {
7+ "type": "integer",
8+ "default": []
9+ }
10+ }
11+ },
12+ "tests": [
13+ {
14+ "description": "valid when property is specified",
15+ "data": {"foo": 13},
16+ "valid": true
17+ },
18+ {
19+ "description": "still valid when the invalid default is used",
20+ "data": {},
21+ "valid": true
22+ }
23+ ]
24+ },
25+ {
26+ "description": "invalid string value for default",
27+ "schema": {
28+ "properties": {
29+ "bar": {
30+ "type": "string",
31+ "minLength": 4,
32+ "default": "bad"
33+ }
34+ }
35+ },
36+ "tests": [
37+ {
38+ "description": "valid when property is specified",
39+ "data": {"bar": "good"},
40+ "valid": true
41+ },
42+ {
43+ "description": "still valid when the invalid default is used",
44+ "data": {},
45+ "valid": true
46+ }
47+ ]
48+ }
49+]
JSON-Schema-Test-Suite/tests/draft6/definitions.jsonnew file+32
@@ -0,0 +1,32 @@
1+[
2+ {
3+ "description": "valid definition",
4+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
5+ "tests": [
6+ {
7+ "description": "valid definition schema",
8+ "data": {
9+ "definitions": {
10+ "foo": {"type": "integer"}
11+ }
12+ },
13+ "valid": true
14+ }
15+ ]
16+ },
17+ {
18+ "description": "invalid definition",
19+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
20+ "tests": [
21+ {
22+ "description": "invalid definition schema",
23+ "data": {
24+ "definitions": {
25+ "foo": {"type": 1}
26+ }
27+ },
28+ "valid": false
29+ }
30+ ]
31+ }
32+]
JSON-Schema-Test-Suite/tests/draft6/dependencies.jsonnew file+162
@@ -0,0 +1,162 @@
1+[
2+ {
3+ "description": "dependencies",
4+ "schema": {
5+ "dependencies": {"bar": ["foo"]}
6+ },
7+ "tests": [
8+ {
9+ "description": "neither",
10+ "data": {},
11+ "valid": true
12+ },
13+ {
14+ "description": "nondependant",
15+ "data": {"foo": 1},
16+ "valid": true
17+ },
18+ {
19+ "description": "with dependency",
20+ "data": {"foo": 1, "bar": 2},
21+ "valid": true
22+ },
23+ {
24+ "description": "missing dependency",
25+ "data": {"bar": 2},
26+ "valid": false
27+ },
28+ {
29+ "description": "ignores non-objects",
30+ "data": "foo",
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "dependencies with empty array",
37+ "schema": {
38+ "dependencies": {"bar": []}
39+ },
40+ "tests": [
41+ {
42+ "description": "empty object",
43+ "data": {},
44+ "valid": true
45+ },
46+ {
47+ "description": "object with one property",
48+ "data": {"bar": 2},
49+ "valid": true
50+ }
51+ ]
52+ },
53+ {
54+ "description": "multiple dependencies",
55+ "schema": {
56+ "dependencies": {"quux": ["foo", "bar"]}
57+ },
58+ "tests": [
59+ {
60+ "description": "neither",
61+ "data": {},
62+ "valid": true
63+ },
64+ {
65+ "description": "nondependants",
66+ "data": {"foo": 1, "bar": 2},
67+ "valid": true
68+ },
69+ {
70+ "description": "with dependencies",
71+ "data": {"foo": 1, "bar": 2, "quux": 3},
72+ "valid": true
73+ },
74+ {
75+ "description": "missing dependency",
76+ "data": {"foo": 1, "quux": 2},
77+ "valid": false
78+ },
79+ {
80+ "description": "missing other dependency",
81+ "data": {"bar": 1, "quux": 2},
82+ "valid": false
83+ },
84+ {
85+ "description": "missing both dependencies",
86+ "data": {"quux": 1},
87+ "valid": false
88+ }
89+ ]
90+ },
91+ {
92+ "description": "multiple dependencies subschema",
93+ "schema": {
94+ "dependencies": {
95+ "bar": {
96+ "properties": {
97+ "foo": {"type": "integer"},
98+ "bar": {"type": "integer"}
99+ }
100+ }
101+ }
102+ },
103+ "tests": [
104+ {
105+ "description": "valid",
106+ "data": {"foo": 1, "bar": 2},
107+ "valid": true
108+ },
109+ {
110+ "description": "no dependency",
111+ "data": {"foo": "quux"},
112+ "valid": true
113+ },
114+ {
115+ "description": "wrong type",
116+ "data": {"foo": "quux", "bar": 2},
117+ "valid": false
118+ },
119+ {
120+ "description": "wrong type other",
121+ "data": {"foo": 2, "bar": "quux"},
122+ "valid": false
123+ },
124+ {
125+ "description": "wrong type both",
126+ "data": {"foo": "quux", "bar": "quux"},
127+ "valid": false
128+ }
129+ ]
130+ },
131+ {
132+ "description": "dependencies with boolean subschemas",
133+ "schema": {
134+ "dependencies": {
135+ "foo": true,
136+ "bar": false
137+ }
138+ },
139+ "tests": [
140+ {
141+ "description": "object with property having schema true is valid",
142+ "data": {"foo": 1},
143+ "valid": true
144+ },
145+ {
146+ "description": "object with property having schema false is invalid",
147+ "data": {"bar": 2},
148+ "valid": false
149+ },
150+ {
151+ "description": "object with both properties is invalid",
152+ "data": {"foo": 1, "bar": 2},
153+ "valid": false
154+ },
155+ {
156+ "description": "empty object is valid",
157+ "data": {},
158+ "valid": true
159+ }
160+ ]
161+ }
162+]
JSON-Schema-Test-Suite/tests/draft6/enum.jsonnew file+72
@@ -0,0 +1,72 @@
1+[
2+ {
3+ "description": "simple enum validation",
4+ "schema": {"enum": [1, 2, 3]},
5+ "tests": [
6+ {
7+ "description": "one of the enum is valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "something else is invalid",
13+ "data": 4,
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "heterogeneous enum validation",
20+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
21+ "tests": [
22+ {
23+ "description": "one of the enum is valid",
24+ "data": [],
25+ "valid": true
26+ },
27+ {
28+ "description": "something else is invalid",
29+ "data": null,
30+ "valid": false
31+ },
32+ {
33+ "description": "objects are deep compared",
34+ "data": {"foo": false},
35+ "valid": false
36+ }
37+ ]
38+ },
39+ {
40+ "description": "enums in properties",
41+ "schema": {
42+ "type":"object",
43+ "properties": {
44+ "foo": {"enum":["foo"]},
45+ "bar": {"enum":["bar"]}
46+ },
47+ "required": ["bar"]
48+ },
49+ "tests": [
50+ {
51+ "description": "both properties are valid",
52+ "data": {"foo":"foo", "bar":"bar"},
53+ "valid": true
54+ },
55+ {
56+ "description": "missing optional property is valid",
57+ "data": {"bar":"bar"},
58+ "valid": true
59+ },
60+ {
61+ "description": "missing required property is invalid",
62+ "data": {"foo":"foo"},
63+ "valid": false
64+ },
65+ {
66+ "description": "missing all properties is invalid",
67+ "data": {},
68+ "valid": false
69+ }
70+ ]
71+ }
72+]
JSON-Schema-Test-Suite/tests/draft6/exclusiveMaximum.jsonnew file+30
@@ -0,0 +1,30 @@
1+[
2+ {
3+ "description": "exclusiveMaximum validation",
4+ "schema": {
5+ "exclusiveMaximum": 3.0
6+ },
7+ "tests": [
8+ {
9+ "description": "below the exclusiveMaximum is valid",
10+ "data": 2.2,
11+ "valid": true
12+ },
13+ {
14+ "description": "boundary point is invalid",
15+ "data": 3.0,
16+ "valid": false
17+ },
18+ {
19+ "description": "above the exclusiveMaximum is invalid",
20+ "data": 3.5,
21+ "valid": false
22+ },
23+ {
24+ "description": "ignores non-numbers",
25+ "data": "x",
26+ "valid": true
27+ }
28+ ]
29+ }
30+]
JSON-Schema-Test-Suite/tests/draft6/exclusiveMinimum.jsonnew file+30
@@ -0,0 +1,30 @@
1+[
2+ {
3+ "description": "exclusiveMinimum validation",
4+ "schema": {
5+ "exclusiveMinimum": 1.1
6+ },
7+ "tests": [
8+ {
9+ "description": "above the exclusiveMinimum is valid",
10+ "data": 1.2,
11+ "valid": true
12+ },
13+ {
14+ "description": "boundary point is invalid",
15+ "data": 1.1,
16+ "valid": false
17+ },
18+ {
19+ "description": "below the exclusiveMinimum is invalid",
20+ "data": 0.6,
21+ "valid": false
22+ },
23+ {
24+ "description": "ignores non-numbers",
25+ "data": "x",
26+ "valid": true
27+ }
28+ ]
29+ }
30+]
JSON-Schema-Test-Suite/tests/draft6/items.jsonnew file+133
@@ -0,0 +1,133 @@
1+[
2+ {
3+ "description": "a schema given for items",
4+ "schema": {
5+ "items": {"type": "integer"}
6+ },
7+ "tests": [
8+ {
9+ "description": "valid items",
10+ "data": [ 1, 2, 3 ],
11+ "valid": true
12+ },
13+ {
14+ "description": "wrong type of items",
15+ "data": [1, "x"],
16+ "valid": false
17+ },
18+ {
19+ "description": "ignores non-arrays",
20+ "data": {"foo" : "bar"},
21+ "valid": true
22+ },
23+ {
24+ "description": "JavaScript pseudo-array is valid",
25+ "data": {
26+ "0": "invalid",
27+ "length": 1
28+ },
29+ "valid": true
30+ }
31+ ]
32+ },
33+ {
34+ "description": "an array of schemas for items",
35+ "schema": {
36+ "items": [
37+ {"type": "integer"},
38+ {"type": "string"}
39+ ]
40+ },
41+ "tests": [
42+ {
43+ "description": "correct types",
44+ "data": [ 1, "foo" ],
45+ "valid": true
46+ },
47+ {
48+ "description": "wrong types",
49+ "data": [ "foo", 1 ],
50+ "valid": false
51+ },
52+ {
53+ "description": "incomplete array of items",
54+ "data": [ 1 ],
55+ "valid": true
56+ },
57+ {
58+ "description": "array with additional items",
59+ "data": [ 1, "foo", true ],
60+ "valid": true
61+ },
62+ {
63+ "description": "empty array",
64+ "data": [ ],
65+ "valid": true
66+ },
67+ {
68+ "description": "JavaScript pseudo-array is valid",
69+ "data": {
70+ "0": "invalid",
71+ "1": "valid",
72+ "length": 2
73+ },
74+ "valid": true
75+ }
76+ ]
77+ },
78+ {
79+ "description": "items with boolean schema (true)",
80+ "schema": {"items": true},
81+ "tests": [
82+ {
83+ "description": "any array is valid",
84+ "data": [ 1, "foo", true ],
85+ "valid": true
86+ },
87+ {
88+ "description": "empty array is valid",
89+ "data": [],
90+ "valid": true
91+ }
92+ ]
93+ },
94+ {
95+ "description": "items with boolean schema (false)",
96+ "schema": {"items": false},
97+ "tests": [
98+ {
99+ "description": "any non-empty array is invalid",
100+ "data": [ 1, "foo", true ],
101+ "valid": false
102+ },
103+ {
104+ "description": "empty array is valid",
105+ "data": [],
106+ "valid": true
107+ }
108+ ]
109+ },
110+ {
111+ "description": "items with boolean schemas",
112+ "schema": {
113+ "items": [true, false]
114+ },
115+ "tests": [
116+ {
117+ "description": "array with one item is valid",
118+ "data": [ 1 ],
119+ "valid": true
120+ },
121+ {
122+ "description": "array with two items is invalid",
123+ "data": [ 1, "foo" ],
124+ "valid": false
125+ },
126+ {
127+ "description": "empty array is valid",
128+ "data": [],
129+ "valid": true
130+ }
131+ ]
132+ }
133+]
JSON-Schema-Test-Suite/tests/draft6/maxItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maxItems validation",
4+ "schema": {"maxItems": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": [1],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1, 2],
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": [1, 2, 3],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "foobar",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/maxLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "maxLength validation",
4+ "schema": {"maxLength": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": "f",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 100,
24+ "valid": true
25+ },
26+ {
27+ "description": "two supplementary Unicode code points is long enough",
28+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
29+ "valid": true
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft6/maxProperties.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maxProperties validation",
4+ "schema": {"maxProperties": 2},
5+ "tests": [
6+ {
7+ "description": "shorter is valid",
8+ "data": {"foo": 1},
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": {"foo": 1, "bar": 2},
14+ "valid": true
15+ },
16+ {
17+ "description": "too long is invalid",
18+ "data": {"foo": 1, "bar": 2, "baz": 3},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": "foobar",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/maximum.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "maximum validation",
4+ "schema": {"maximum": 3.0},
5+ "tests": [
6+ {
7+ "description": "below the maximum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "boundary point is valid",
13+ "data": 3.0,
14+ "valid": true
15+ },
16+ {
17+ "description": "above the maximum is invalid",
18+ "data": 3.5,
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-numbers",
23+ "data": "x",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/minItems.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minItems validation",
4+ "schema": {"minItems": 1},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": [1],
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": [],
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-arrays",
23+ "data": "",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/minLength.jsonnew file+33
@@ -0,0 +1,33 @@
1+[
2+ {
3+ "description": "minLength validation",
4+ "schema": {"minLength": 2},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": "foo",
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": "fo",
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": "f",
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-strings",
23+ "data": 1,
24+ "valid": true
25+ },
26+ {
27+ "description": "one supplementary Unicode code point is not long enough",
28+ "data": "\uD83D\uDCA9",
29+ "valid": false
30+ }
31+ ]
32+ }
33+]
JSON-Schema-Test-Suite/tests/draft6/minProperties.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minProperties validation",
4+ "schema": {"minProperties": 1},
5+ "tests": [
6+ {
7+ "description": "longer is valid",
8+ "data": {"foo": 1, "bar": 2},
9+ "valid": true
10+ },
11+ {
12+ "description": "exact length is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "too short is invalid",
18+ "data": {},
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-objects",
23+ "data": "",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/minimum.jsonnew file+28
@@ -0,0 +1,28 @@
1+[
2+ {
3+ "description": "minimum validation",
4+ "schema": {"minimum": 1.1},
5+ "tests": [
6+ {
7+ "description": "above the minimum is valid",
8+ "data": 2.6,
9+ "valid": true
10+ },
11+ {
12+ "description": "boundary point is valid",
13+ "data": 1.1,
14+ "valid": true
15+ },
16+ {
17+ "description": "below the minimum is invalid",
18+ "data": 0.6,
19+ "valid": false
20+ },
21+ {
22+ "description": "ignores non-numbers",
23+ "data": "x",
24+ "valid": true
25+ }
26+ ]
27+ }
28+]
JSON-Schema-Test-Suite/tests/draft6/multipleOf.jsonnew file+60
@@ -0,0 +1,60 @@
1+[
2+ {
3+ "description": "by int",
4+ "schema": {"multipleOf": 2},
5+ "tests": [
6+ {
7+ "description": "int by int",
8+ "data": 10,
9+ "valid": true
10+ },
11+ {
12+ "description": "int by int fail",
13+ "data": 7,
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-numbers",
18+ "data": "foo",
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "by number",
25+ "schema": {"multipleOf": 1.5},
26+ "tests": [
27+ {
28+ "description": "zero is multiple of anything",
29+ "data": 0,
30+ "valid": true
31+ },
32+ {
33+ "description": "4.5 is multiple of 1.5",
34+ "data": 4.5,
35+ "valid": true
36+ },
37+ {
38+ "description": "35 is not multiple of 1.5",
39+ "data": 35,
40+ "valid": false
41+ }
42+ ]
43+ },
44+ {
45+ "description": "by small number",
46+ "schema": {"multipleOf": 0.0001},
47+ "tests": [
48+ {
49+ "description": "0.0075 is multiple of 0.0001",
50+ "data": 0.0075,
51+ "valid": true
52+ },
53+ {
54+ "description": "0.00751 is not multiple of 0.0001",
55+ "data": 0.00751,
56+ "valid": false
57+ }
58+ ]
59+ }
60+]
JSON-Schema-Test-Suite/tests/draft6/not.jsonnew file+117
@@ -0,0 +1,117 @@
1+[
2+ {
3+ "description": "not",
4+ "schema": {
5+ "not": {"type": "integer"}
6+ },
7+ "tests": [
8+ {
9+ "description": "allowed",
10+ "data": "foo",
11+ "valid": true
12+ },
13+ {
14+ "description": "disallowed",
15+ "data": 1,
16+ "valid": false
17+ }
18+ ]
19+ },
20+ {
21+ "description": "not multiple types",
22+ "schema": {
23+ "not": {"type": ["integer", "boolean"]}
24+ },
25+ "tests": [
26+ {
27+ "description": "valid",
28+ "data": "foo",
29+ "valid": true
30+ },
31+ {
32+ "description": "mismatch",
33+ "data": 1,
34+ "valid": false
35+ },
36+ {
37+ "description": "other mismatch",
38+ "data": true,
39+ "valid": false
40+ }
41+ ]
42+ },
43+ {
44+ "description": "not more complex schema",
45+ "schema": {
46+ "not": {
47+ "type": "object",
48+ "properties": {
49+ "foo": {
50+ "type": "string"
51+ }
52+ }
53+ }
54+ },
55+ "tests": [
56+ {
57+ "description": "match",
58+ "data": 1,
59+ "valid": true
60+ },
61+ {
62+ "description": "other match",
63+ "data": {"foo": 1},
64+ "valid": true
65+ },
66+ {
67+ "description": "mismatch",
68+ "data": {"foo": "bar"},
69+ "valid": false
70+ }
71+ ]
72+ },
73+ {
74+ "description": "forbidden property",
75+ "schema": {
76+ "properties": {
77+ "foo": {
78+ "not": {}
79+ }
80+ }
81+ },
82+ "tests": [
83+ {
84+ "description": "property present",
85+ "data": {"foo": 1, "bar": 2},
86+ "valid": false
87+ },
88+ {
89+ "description": "property absent",
90+ "data": {"bar": 1, "baz": 2},
91+ "valid": true
92+ }
93+ ]
94+ },
95+ {
96+ "description": "not with boolean schema true",
97+ "schema": {"not": true},
98+ "tests": [
99+ {
100+ "description": "any value is invalid",
101+ "data": "foo",
102+ "valid": false
103+ }
104+ ]
105+ },
106+ {
107+ "description": "not with boolean schema false",
108+ "schema": {"not": false},
109+ "tests": [
110+ {
111+ "description": "any value is valid",
112+ "data": "foo",
113+ "valid": true
114+ }
115+ ]
116+ }
117+]
JSON-Schema-Test-Suite/tests/draft6/oneOf.jsonnew file+112
@@ -0,0 +1,112 @@
1+[
2+ {
3+ "description": "oneOf",
4+ "schema": {
5+ "oneOf": [
6+ {
7+ "type": "integer"
8+ },
9+ {
10+ "minimum": 2
11+ }
12+ ]
13+ },
14+ "tests": [
15+ {
16+ "description": "first oneOf valid",
17+ "data": 1,
18+ "valid": true
19+ },
20+ {
21+ "description": "second oneOf valid",
22+ "data": 2.5,
23+ "valid": true
24+ },
25+ {
26+ "description": "both oneOf valid",
27+ "data": 3,
28+ "valid": false
29+ },
30+ {
31+ "description": "neither oneOf valid",
32+ "data": 1.5,
33+ "valid": false
34+ }
35+ ]
36+ },
37+ {
38+ "description": "oneOf with base schema",
39+ "schema": {
40+ "type": "string",
41+ "oneOf" : [
42+ {
43+ "minLength": 2
44+ },
45+ {
46+ "maxLength": 4
47+ }
48+ ]
49+ },
50+ "tests": [
51+ {
52+ "description": "mismatch base schema",
53+ "data": 3,
54+ "valid": false
55+ },
56+ {
57+ "description": "one oneOf valid",
58+ "data": "foobar",
59+ "valid": true
60+ },
61+ {
62+ "description": "both oneOf valid",
63+ "data": "foo",
64+ "valid": false
65+ }
66+ ]
67+ },
68+ {
69+ "description": "oneOf with boolean schemas, all true",
70+ "schema": {"oneOf": [true, true, true]},
71+ "tests": [
72+ {
73+ "description": "any value is invalid",
74+ "data": "foo",
75+ "valid": false
76+ }
77+ ]
78+ },
79+ {
80+ "description": "oneOf with boolean schemas, one true",
81+ "schema": {"oneOf": [true, false, false]},
82+ "tests": [
83+ {
84+ "description": "any value is valid",
85+ "data": "foo",
86+ "valid": true
87+ }
88+ ]
89+ },
90+ {
91+ "description": "oneOf with boolean schemas, more than one true",
92+ "schema": {"oneOf": [true, true, false]},
93+ "tests": [
94+ {
95+ "description": "any value is invalid",
96+ "data": "foo",
97+ "valid": false
98+ }
99+ ]
100+ },
101+ {
102+ "description": "oneOf with boolean schemas, all false",
103+ "schema": {"oneOf": [false, false, false]},
104+ "tests": [
105+ {
106+ "description": "any value is invalid",
107+ "data": "foo",
108+ "valid": false
109+ }
110+ ]
111+ }
112+]
JSON-Schema-Test-Suite/tests/draft6/optional/bignum.jsonnew file+105
@@ -0,0 +1,105 @@
1+[
2+ {
3+ "description": "integer",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "a bignum is an integer",
8+ "data": 12345678910111213141516171819202122232425262728293031,
9+ "valid": true
10+ }
11+ ]
12+ },
13+ {
14+ "description": "number",
15+ "schema": {"type": "number"},
16+ "tests": [
17+ {
18+ "description": "a bignum is a number",
19+ "data": 98249283749234923498293171823948729348710298301928331,
20+ "valid": true
21+ }
22+ ]
23+ },
24+ {
25+ "description": "integer",
26+ "schema": {"type": "integer"},
27+ "tests": [
28+ {
29+ "description": "a negative bignum is an integer",
30+ "data": -12345678910111213141516171819202122232425262728293031,
31+ "valid": true
32+ }
33+ ]
34+ },
35+ {
36+ "description": "number",
37+ "schema": {"type": "number"},
38+ "tests": [
39+ {
40+ "description": "a negative bignum is a number",
41+ "data": -98249283749234923498293171823948729348710298301928331,
42+ "valid": true
43+ }
44+ ]
45+ },
46+ {
47+ "description": "string",
48+ "schema": {"type": "string"},
49+ "tests": [
50+ {
51+ "description": "a bignum is not a string",
52+ "data": 98249283749234923498293171823948729348710298301928331,
53+ "valid": false
54+ }
55+ ]
56+ },
57+ {
58+ "description": "integer comparison",
59+ "schema": {"maximum": 18446744073709551615},
60+ "tests": [
61+ {
62+ "description": "comparison works for high numbers",
63+ "data": 18446744073709551600,
64+ "valid": true
65+ }
66+ ]
67+ },
68+ {
69+ "description": "float comparison with high precision",
70+ "schema": {
71+ "exclusiveMaximum": 972783798187987123879878123.18878137
72+ },
73+ "tests": [
74+ {
75+ "description": "comparison works for high numbers",
76+ "data": 972783798187987123879878123.188781371,
77+ "valid": false
78+ }
79+ ]
80+ },
81+ {
82+ "description": "integer comparison",
83+ "schema": {"minimum": -18446744073709551615},
84+ "tests": [
85+ {
86+ "description": "comparison works for very negative numbers",
87+ "data": -18446744073709551600,
88+ "valid": true
89+ }
90+ ]
91+ },
92+ {
93+ "description": "float comparison with high precision on negative numbers",
94+ "schema": {
95+ "exclusiveMinimum": -972783798187987123879878123.18878137
96+ },
97+ "tests": [
98+ {
99+ "description": "comparison works for very negative numbers",
100+ "data": -972783798187987123879878123.188781371,
101+ "valid": false
102+ }
103+ ]
104+ }
105+]
JSON-Schema-Test-Suite/tests/draft6/optional/ecmascript-regex.jsonnew file+13
@@ -0,0 +1,13 @@
1+[
2+ {
3+ "description": "ECMA 262 regex non-compliance",
4+ "schema": { "format": "regex" },
5+ "tests": [
6+ {
7+ "description": "ECMA 262 has no support for \\Z anchor from .NET",
8+ "data": "^\\S(|(.|\\n)*\\S)\\Z",
9+ "valid": false
10+ }
11+ ]
12+ }
13+]
JSON-Schema-Test-Suite/tests/draft6/optional/format.jsonnew file+148
@@ -0,0 +1,148 @@
1+[
2+ {
3+ "description": "validation of date-time strings",
4+ "schema": {"format": "date-time"},
5+ "tests": [
6+ {
7+ "description": "a valid date-time string",
8+ "data": "1963-06-19T08:30:06.283185Z",
9+ "valid": true
10+ },
11+ {
12+ "description": "an invalid date-time string",
13+ "data": "06/19/1963 08:30:06 PST",
14+ "valid": false
15+ },
16+ {
17+ "description": "only RFC3339 not all of ISO 8601 are valid",
18+ "data": "2013-350T01:01:01",
19+ "valid": false
20+ }
21+ ]
22+ },
23+ {
24+ "description": "validation of URIs",
25+ "schema": {"format": "uri"},
26+ "tests": [
27+ {
28+ "description": "a valid URI",
29+ "data": "http://foo.bar/?baz=qux#quux",
30+ "valid": true
31+ },
32+ {
33+ "description": "an invalid protocol-relative URI Reference",
34+ "data": "//foo.bar/?baz=qux#quux",
35+ "valid": false
36+ },
37+ {
38+ "description": "an invalid URI",
39+ "data": "\\\\WINDOWS\\fileshare",
40+ "valid": false
41+ },
42+ {
43+ "description": "an invalid URI though valid URI reference",
44+ "data": "abc",
45+ "valid": false
46+ }
47+ ]
48+ },
49+ {
50+ "description": "validation of e-mail addresses",
51+ "schema": {"format": "email"},
52+ "tests": [
53+ {
54+ "description": "a valid e-mail address",
55+ "data": "joe.bloggs@example.com",
56+ "valid": true
57+ },
58+ {
59+ "description": "an invalid e-mail address",
60+ "data": "2962",
61+ "valid": false
62+ }
63+ ]
64+ },
65+ {
66+ "description": "validation of IP addresses",
67+ "schema": {"format": "ipv4"},
68+ "tests": [
69+ {
70+ "description": "a valid IP address",
71+ "data": "192.168.0.1",
72+ "valid": true
73+ },
74+ {
75+ "description": "an IP address with too many components",
76+ "data": "127.0.0.0.1",
77+ "valid": false
78+ },
79+ {
80+ "description": "an IP address with out-of-range values",
81+ "data": "256.256.256.256",
82+ "valid": false
83+ },
84+ {
85+ "description": "an IP address without 4 components",
86+ "data": "127.0",
87+ "valid": false
88+ },
89+ {
90+ "description": "an IP address as an integer",
91+ "data": "0x7f000001",
92+ "valid": false
93+ }
94+ ]
95+ },
96+ {
97+ "description": "validation of IPv6 addresses",
98+ "schema": {"format": "ipv6"},
99+ "tests": [
100+ {
101+ "description": "a valid IPv6 address",
102+ "data": "::1",
103+ "valid": true
104+ },
105+ {
106+ "description": "an IPv6 address with out-of-range values",
107+ "data": "12345::",
108+ "valid": false
109+ },
110+ {
111+ "description": "an IPv6 address with too many components",
112+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
113+ "valid": false
114+ },
115+ {
116+ "description": "an IPv6 address containing illegal characters",
117+ "data": "::laptop",
118+ "valid": false
119+ }
120+ ]
121+ },
122+ {
123+ "description": "validation of host names",
124+ "schema": {"format": "hostname"},
125+ "tests": [
126+ {
127+ "description": "a valid host name",
128+ "data": "www.example.com",
129+ "valid": true
130+ },
131+ {
132+ "description": "a host name starting with an illegal character",
133+ "data": "-a-host-name-that-starts-with--",
134+ "valid": false
135+ },
136+ {
137+ "description": "a host name containing illegal characters",
138+ "data": "not_a_valid_host_name",
139+ "valid": false
140+ },
141+ {
142+ "description": "a host name with a component too long",
143+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
144+ "valid": false
145+ }
146+ ]
147+ }
148+]
JSON-Schema-Test-Suite/tests/draft6/optional/zeroTerminatedFloats.jsonnew file+15
@@ -0,0 +1,15 @@
1+[
2+ {
3+ "description": "some languages do not distinguish between different types of numeric value",
4+ "schema": {
5+ "type": "integer"
6+ },
7+ "tests": [
8+ {
9+ "description": "a float is not an integer even without fractional part",
10+ "data": 1.0,
11+ "valid": true
12+ }
13+ ]
14+ }
15+]
JSON-Schema-Test-Suite/tests/draft6/pattern.jsonnew file+34
@@ -0,0 +1,34 @@
1+[
2+ {
3+ "description": "pattern validation",
4+ "schema": {"pattern": "^a*$"},
5+ "tests": [
6+ {
7+ "description": "a matching pattern is valid",
8+ "data": "aaa",
9+ "valid": true
10+ },
11+ {
12+ "description": "a non-matching pattern is invalid",
13+ "data": "abc",
14+ "valid": false
15+ },
16+ {
17+ "description": "ignores non-strings",
18+ "data": true,
19+ "valid": true
20+ }
21+ ]
22+ },
23+ {
24+ "description": "pattern is not anchored",
25+ "schema": {"pattern": "a+"},
26+ "tests": [
27+ {
28+ "description": "matches a substring",
29+ "data": "xxaayy",
30+ "valid": true
31+ }
32+ ]
33+ }
34+]
JSON-Schema-Test-Suite/tests/draft6/patternProperties.jsonnew file+141
@@ -0,0 +1,141 @@
1+[
2+ {
3+ "description":
4+ "patternProperties validates properties matching a regex",
5+ "schema": {
6+ "patternProperties": {
7+ "f.*o": {"type": "integer"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "a single valid match is valid",
13+ "data": {"foo": 1},
14+ "valid": true
15+ },
16+ {
17+ "description": "multiple valid matches is valid",
18+ "data": {"foo": 1, "foooooo" : 2},
19+ "valid": true
20+ },
21+ {
22+ "description": "a single invalid match is invalid",
23+ "data": {"foo": "bar", "fooooo": 2},
24+ "valid": false
25+ },
26+ {
27+ "description": "multiple invalid matches is invalid",
28+ "data": {"foo": "bar", "foooooo" : "baz"},
29+ "valid": false
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": 12,
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description": "multiple simultaneous patternProperties are validated",
40+ "schema": {
41+ "patternProperties": {
42+ "a*": {"type": "integer"},
43+ "aaa*": {"maximum": 20}
44+ }
45+ },
46+ "tests": [
47+ {
48+ "description": "a single valid match is valid",
49+ "data": {"a": 21},
50+ "valid": true
51+ },
52+ {
53+ "description": "a simultaneous match is valid",
54+ "data": {"aaaa": 18},
55+ "valid": true
56+ },
57+ {
58+ "description": "multiple matches is valid",
59+ "data": {"a": 21, "aaaa": 18},
60+ "valid": true
61+ },
62+ {
63+ "description": "an invalid due to one is invalid",
64+ "data": {"a": "bar"},
65+ "valid": false
66+ },
67+ {
68+ "description": "an invalid due to the other is invalid",
69+ "data": {"aaaa": 31},
70+ "valid": false
71+ },
72+ {
73+ "description": "an invalid due to both is invalid",
74+ "data": {"aaa": "foo", "aaaa": 31},
75+ "valid": false
76+ }
77+ ]
78+ },
79+ {
80+ "description": "regexes are not anchored by default and are case sensitive",
81+ "schema": {
82+ "patternProperties": {
83+ "[0-9]{2,}": { "type": "boolean" },
84+ "X_": { "type": "string" }
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "non recognized members are ignored",
90+ "data": { "answer 1": "42" },
91+ "valid": true
92+ },
93+ {
94+ "description": "recognized members are accounted for",
95+ "data": { "a31b": null },
96+ "valid": false
97+ },
98+ {
99+ "description": "regexes are case sensitive",
100+ "data": { "a_x_3": 3 },
101+ "valid": true
102+ },
103+ {
104+ "description": "regexes are case sensitive, 2",
105+ "data": { "a_X_3": 3 },
106+ "valid": false
107+ }
108+ ]
109+ },
110+ {
111+ "description": "patternProperties with boolean schemas",
112+ "schema": {
113+ "patternProperties": {
114+ "f.*": true,
115+ "b.*": false
116+ }
117+ },
118+ "tests": [
119+ {
120+ "description": "object with property matching schema true is valid",
121+ "data": {"foo": 1},
122+ "valid": true
123+ },
124+ {
125+ "description": "object with property matching schema false is invalid",
126+ "data": {"bar": 2},
127+ "valid": false
128+ },
129+ {
130+ "description": "object with both properties is invalid",
131+ "data": {"foo": 1, "bar": 2},
132+ "valid": false
133+ },
134+ {
135+ "description": "empty object is valid",
136+ "data": {},
137+ "valid": true
138+ }
139+ ]
140+ }
141+]
JSON-Schema-Test-Suite/tests/draft6/properties.jsonnew file+123
@@ -0,0 +1,123 @@
1+[
2+ {
3+ "description": "object properties validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {"type": "integer"},
7+ "bar": {"type": "string"}
8+ }
9+ },
10+ "tests": [
11+ {
12+ "description": "both properties present and valid is valid",
13+ "data": {"foo": 1, "bar": "baz"},
14+ "valid": true
15+ },
16+ {
17+ "description": "one property invalid is invalid",
18+ "data": {"foo": 1, "bar": {}},
19+ "valid": false
20+ },
21+ {
22+ "description": "both properties invalid is invalid",
23+ "data": {"foo": [], "bar": {}},
24+ "valid": false
25+ },
26+ {
27+ "description": "doesn't invalidate other properties",
28+ "data": {"quux": []},
29+ "valid": true
30+ },
31+ {
32+ "description": "ignores non-objects",
33+ "data": [],
34+ "valid": true
35+ }
36+ ]
37+ },
38+ {
39+ "description":
40+ "properties, patternProperties, additionalProperties interaction",
41+ "schema": {
42+ "properties": {
43+ "foo": {"type": "array", "maxItems": 3},
44+ "bar": {"type": "array"}
45+ },
46+ "patternProperties": {"f.o": {"minItems": 2}},
47+ "additionalProperties": {"type": "integer"}
48+ },
49+ "tests": [
50+ {
51+ "description": "property validates property",
52+ "data": {"foo": [1, 2]},
53+ "valid": true
54+ },
55+ {
56+ "description": "property invalidates property",
57+ "data": {"foo": [1, 2, 3, 4]},
58+ "valid": false
59+ },
60+ {
61+ "description": "patternProperty invalidates property",
62+ "data": {"foo": []},
63+ "valid": false
64+ },
65+ {
66+ "description": "patternProperty validates nonproperty",
67+ "data": {"fxo": [1, 2]},
68+ "valid": true
69+ },
70+ {
71+ "description": "patternProperty invalidates nonproperty",
72+ "data": {"fxo": []},
73+ "valid": false
74+ },
75+ {
76+ "description": "additionalProperty ignores property",
77+ "data": {"bar": []},
78+ "valid": true
79+ },
80+ {
81+ "description": "additionalProperty validates others",
82+ "data": {"quux": 3},
83+ "valid": true
84+ },
85+ {
86+ "description": "additionalProperty invalidates others",
87+ "data": {"quux": "foo"},
88+ "valid": false
89+ }
90+ ]
91+ },
92+ {
93+ "description": "properties with boolean schema",
94+ "schema": {
95+ "properties": {
96+ "foo": true,
97+ "bar": false
98+ }
99+ },
100+ "tests": [
101+ {
102+ "description": "no property present is valid",
103+ "data": {},
104+ "valid": true
105+ },
106+ {
107+ "description": "only 'true' property present is valid",
108+ "data": {"foo": 1},
109+ "valid": true
110+ },
111+ {
112+ "description": "only 'false' property present is invalid",
113+ "data": {"bar": 2},
114+ "valid": false
115+ },
116+ {
117+ "description": "both properties present is invalid",
118+ "data": {"foo": 1, "bar": 2},
119+ "valid": false
120+ }
121+ ]
122+ }
123+]
JSON-Schema-Test-Suite/tests/draft6/propertyNames.jsonnew file+68
@@ -0,0 +1,68 @@
1+[
2+ {
3+ "description": "propertyNames validation",
4+ "schema": {
5+ "propertyNames": {"maxLength": 3}
6+ },
7+ "tests": [
8+ {
9+ "description": "all property names valid",
10+ "data": {
11+ "f": {},
12+ "foo": {}
13+ },
14+ "valid": true
15+ },
16+ {
17+ "description": "some property names invalid",
18+ "data": {
19+ "foo": {},
20+ "foobar": {}
21+ },
22+ "valid": false
23+ },
24+ {
25+ "description": "object without properties is valid",
26+ "data": {},
27+ "valid": true
28+ },
29+ {
30+ "description": "non-object is valid",
31+ "data": [],
32+ "valid": true
33+ }
34+ ]
35+ },
36+ {
37+ "description": "propertyNames with boolean schema true",
38+ "schema": {"propertyNames": true},
39+ "tests": [
40+ {
41+ "description": "object with any properties is valid",
42+ "data": {"foo": 1},
43+ "valid": true
44+ },
45+ {
46+ "description": "empty object is valid",
47+ "data": {},
48+ "valid": true
49+ }
50+ ]
51+ },
52+ {
53+ "description": "propertyNames with boolean schema false",
54+ "schema": {"propertyNames": false},
55+ "tests": [
56+ {
57+ "description": "object with any properties is invalid",
58+ "data": {"foo": 1},
59+ "valid": false
60+ },
61+ {
62+ "description": "empty object is valid",
63+ "data": {},
64+ "valid": true
65+ }
66+ ]
67+ }
68+]
JSON-Schema-Test-Suite/tests/draft6/ref.jsonnew file+332
@@ -0,0 +1,332 @@
1+[
2+ {
3+ "description": "root pointer ref",
4+ "schema": {
5+ "properties": {
6+ "foo": {"$ref": "#"}
7+ },
8+ "additionalProperties": false
9+ },
10+ "tests": [
11+ {
12+ "description": "match",
13+ "data": {"foo": false},
14+ "valid": true
15+ },
16+ {
17+ "description": "recursive match",
18+ "data": {"foo": {"foo": false}},
19+ "valid": true
20+ },
21+ {
22+ "description": "mismatch",
23+ "data": {"bar": false},
24+ "valid": false
25+ },
26+ {
27+ "description": "recursive mismatch",
28+ "data": {"foo": {"bar": false}},
29+ "valid": false
30+ }
31+ ]
32+ },
33+ {
34+ "description": "relative pointer ref to object",
35+ "schema": {
36+ "properties": {
37+ "foo": {"type": "integer"},
38+ "bar": {"$ref": "#/properties/foo"}
39+ }
40+ },
41+ "tests": [
42+ {
43+ "description": "match",
44+ "data": {"bar": 3},
45+ "valid": true
46+ },
47+ {
48+ "description": "mismatch",
49+ "data": {"bar": true},
50+ "valid": false
51+ }
52+ ]
53+ },
54+ {
55+ "description": "relative pointer ref to array",
56+ "schema": {
57+ "items": [
58+ {"type": "integer"},
59+ {"$ref": "#/items/0"}
60+ ]
61+ },
62+ "tests": [
63+ {
64+ "description": "match array",
65+ "data": [1, 2],
66+ "valid": true
67+ },
68+ {
69+ "description": "mismatch array",
70+ "data": [1, "foo"],
71+ "valid": false
72+ }
73+ ]
74+ },
75+ {
76+ "description": "escaped pointer ref",
77+ "schema": {
78+ "tilda~field": {"type": "integer"},
79+ "slash/field": {"type": "integer"},
80+ "percent%field": {"type": "integer"},
81+ "properties": {
82+ "tilda": {"$ref": "#/tilda~0field"},
83+ "slash": {"$ref": "#/slash~1field"},
84+ "percent": {"$ref": "#/percent%25field"}
85+ }
86+ },
87+ "tests": [
88+ {
89+ "description": "slash invalid",
90+ "data": {"slash": "aoeu"},
91+ "valid": false
92+ },
93+ {
94+ "description": "tilda invalid",
95+ "data": {"tilda": "aoeu"},
96+ "valid": false
97+ },
98+ {
99+ "description": "percent invalid",
100+ "data": {"percent": "aoeu"},
101+ "valid": false
102+ },
103+ {
104+ "description": "slash valid",
105+ "data": {"slash": 123},
106+ "valid": true
107+ },
108+ {
109+ "description": "tilda valid",
110+ "data": {"tilda": 123},
111+ "valid": true
112+ },
113+ {
114+ "description": "percent valid",
115+ "data": {"percent": 123},
116+ "valid": true
117+ }
118+ ]
119+ },
120+ {
121+ "description": "nested refs",
122+ "schema": {
123+ "definitions": {
124+ "a": {"type": "integer"},
125+ "b": {"$ref": "#/definitions/a"},
126+ "c": {"$ref": "#/definitions/b"}
127+ },
128+ "$ref": "#/definitions/c"
129+ },
130+ "tests": [
131+ {
132+ "description": "nested ref valid",
133+ "data": 5,
134+ "valid": true
135+ },
136+ {
137+ "description": "nested ref invalid",
138+ "data": "a",
139+ "valid": false
140+ }
141+ ]
142+ },
143+ {
144+ "description": "ref overrides any sibling keywords",
145+ "schema": {
146+ "definitions": {
147+ "reffed": {
148+ "type": "array"
149+ }
150+ },
151+ "properties": {
152+ "foo": {
153+ "$ref": "#/definitions/reffed",
154+ "maxItems": 2
155+ }
156+ }
157+ },
158+ "tests": [
159+ {
160+ "description": "ref valid",
161+ "data": { "foo": [] },
162+ "valid": true
163+ },
164+ {
165+ "description": "ref valid, maxItems ignored",
166+ "data": { "foo": [ 1, 2, 3] },
167+ "valid": true
168+ },
169+ {
170+ "description": "ref invalid",
171+ "data": { "foo": "string" },
172+ "valid": false
173+ }
174+ ]
175+ },
176+ {
177+ "description": "remote ref, containing refs itself",
178+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
179+ "tests": [
180+ {
181+ "description": "remote ref valid",
182+ "data": {"minLength": 1},
183+ "valid": true
184+ },
185+ {
186+ "description": "remote ref invalid",
187+ "data": {"minLength": -1},
188+ "valid": false
189+ }
190+ ]
191+ },
192+ {
193+ "description": "property named $ref that is not a reference",
194+ "schema": {
195+ "properties": {
196+ "$ref": {"type": "string"}
197+ }
198+ },
199+ "tests": [
200+ {
201+ "description": "property named $ref valid",
202+ "data": {"$ref": "a"},
203+ "valid": true
204+ },
205+ {
206+ "description": "property named $ref invalid",
207+ "data": {"$ref": 2},
208+ "valid": false
209+ }
210+ ]
211+ },
212+ {
213+ "description": "$ref to boolean schema true",
214+ "schema": {
215+ "$ref": "#/definitions/bool",
216+ "definitions": {
217+ "bool": true
218+ }
219+ },
220+ "tests": [
221+ {
222+ "description": "any value is valid",
223+ "data": "foo",
224+ "valid": true
225+ }
226+ ]
227+ },
228+ {
229+ "description": "$ref to boolean schema false",
230+ "schema": {
231+ "$ref": "#/definitions/bool",
232+ "definitions": {
233+ "bool": false
234+ }
235+ },
236+ "tests": [
237+ {
238+ "description": "any value is invalid",
239+ "data": "foo",
240+ "valid": false
241+ }
242+ ]
243+ },
244+ {
245+ "description": "Recursive references between schemas",
246+ "schema": {
247+ "$id": "http://localhost:1234/tree",
248+ "description": "tree of nodes",
249+ "type": "object",
250+ "properties": {
251+ "meta": {"type": "string"},
252+ "nodes": {
253+ "type": "array",
254+ "items": {"$ref": "node"}
255+ }
256+ },
257+ "required": ["meta", "nodes"],
258+ "definitions": {
259+ "node": {
260+ "$id": "http://localhost:1234/node",
261+ "description": "node",
262+ "type": "object",
263+ "properties": {
264+ "value": {"type": "number"},
265+ "subtree": {"$ref": "tree"}
266+ },
267+ "required": ["value"]
268+ }
269+ }
270+ },
271+ "tests": [
272+ {
273+ "description": "valid tree",
274+ "data": {
275+ "meta": "root",
276+ "nodes": [
277+ {
278+ "value": 1,
279+ "subtree": {
280+ "meta": "child",
281+ "nodes": [
282+ {"value": 1.1},
283+ {"value": 1.2}
284+ ]
285+ }
286+ },
287+ {
288+ "value": 2,
289+ "subtree": {
290+ "meta": "child",
291+ "nodes": [
292+ {"value": 2.1},
293+ {"value": 2.2}
294+ ]
295+ }
296+ }
297+ ]
298+ },
299+ "valid": true
300+ },
301+ {
302+ "description": "invalid tree",
303+ "data": {
304+ "meta": "root",
305+ "nodes": [
306+ {
307+ "value": 1,
308+ "subtree": {
309+ "meta": "child",
310+ "nodes": [
311+ {"value": "string is invalid"},
312+ {"value": 1.2}
313+ ]
314+ }
315+ },
316+ {
317+ "value": 2,
318+ "subtree": {
319+ "meta": "child",
320+ "nodes": [
321+ {"value": 2.1},
322+ {"value": 2.2}
323+ ]
324+ }
325+ }
326+ ]
327+ },
328+ "valid": false
329+ }
330+ ]
331+ }
332+]
JSON-Schema-Test-Suite/tests/draft6/refRemote.jsonnew file+171
@@ -0,0 +1,171 @@
1+[
2+ {
3+ "description": "remote ref",
4+ "schema": {"$ref": "http://localhost:1234/integer.json"},
5+ "tests": [
6+ {
7+ "description": "remote ref valid",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "remote ref invalid",
13+ "data": "a",
14+ "valid": false
15+ }
16+ ]
17+ },
18+ {
19+ "description": "fragment within remote ref",
20+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
21+ "tests": [
22+ {
23+ "description": "remote fragment valid",
24+ "data": 1,
25+ "valid": true
26+ },
27+ {
28+ "description": "remote fragment invalid",
29+ "data": "a",
30+ "valid": false
31+ }
32+ ]
33+ },
34+ {
35+ "description": "ref within remote ref",
36+ "schema": {
37+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
38+ },
39+ "tests": [
40+ {
41+ "description": "ref within ref valid",
42+ "data": 1,
43+ "valid": true
44+ },
45+ {
46+ "description": "ref within ref invalid",
47+ "data": "a",
48+ "valid": false
49+ }
50+ ]
51+ },
52+ {
53+ "description": "base URI change",
54+ "schema": {
55+ "$id": "http://localhost:1234/",
56+ "items": {
57+ "$id": "folder/",
58+ "items": {"$ref": "folderInteger.json"}
59+ }
60+ },
61+ "tests": [
62+ {
63+ "description": "base URI change ref valid",
64+ "data": [[1]],
65+ "valid": true
66+ },
67+ {
68+ "description": "base URI change ref invalid",
69+ "data": [["a"]],
70+ "valid": false
71+ }
72+ ]
73+ },
74+ {
75+ "description": "base URI change - change folder",
76+ "schema": {
77+ "$id": "http://localhost:1234/scope_change_defs1.json",
78+ "type" : "object",
79+ "properties": {
80+ "list": {"$ref": "#/definitions/baz"}
81+ },
82+ "definitions": {
83+ "baz": {
84+ "$id": "folder/",
85+ "type": "array",
86+ "items": {"$ref": "folderInteger.json"}
87+ }
88+ }
89+ },
90+ "tests": [
91+ {
92+ "description": "number is valid",
93+ "data": {"list": [1]},
94+ "valid": true
95+ },
96+ {
97+ "description": "string is invalid",
98+ "data": {"list": ["a"]},
99+ "valid": false
100+ }
101+ ]
102+ },
103+ {
104+ "description": "base URI change - change folder in subschema",
105+ "schema": {
106+ "$id": "http://localhost:1234/scope_change_defs2.json",
107+ "type" : "object",
108+ "properties": {
109+ "list": {"$ref": "#/definitions/baz/definitions/bar"}
110+ },
111+ "definitions": {
112+ "baz": {
113+ "$id": "folder/",
114+ "definitions": {
115+ "bar": {
116+ "type": "array",
117+ "items": {"$ref": "folderInteger.json"}
118+ }
119+ }
120+ }
121+ }
122+ },
123+ "tests": [
124+ {
125+ "description": "number is valid",
126+ "data": {"list": [1]},
127+ "valid": true
128+ },
129+ {
130+ "description": "string is invalid",
131+ "data": {"list": ["a"]},
132+ "valid": false
133+ }
134+ ]
135+ },
136+ {
137+ "description": "root ref in remote ref",
138+ "schema": {
139+ "$id": "http://localhost:1234/object",
140+ "type": "object",
141+ "properties": {
142+ "name": {"$ref": "name.json#/definitions/orNull"}
143+ }
144+ },
145+ "tests": [
146+ {
147+ "description": "string is valid",
148+ "data": {
149+ "name": "foo"
150+ },
151+ "valid": true
152+ },
153+ {
154+ "description": "null is valid",
155+ "data": {
156+ "name": null
157+ },
158+ "valid": true
159+ },
160+ {
161+ "description": "object is invalid",
162+ "data": {
163+ "name": {
164+ "name": null
165+ }
166+ },
167+ "valid": false
168+ }
169+ ]
170+ }
171+]
JSON-Schema-Test-Suite/tests/draft6/required.jsonnew file+60
@@ -0,0 +1,60 @@
1+[
2+ {
3+ "description": "required validation",
4+ "schema": {
5+ "properties": {
6+ "foo": {},
7+ "bar": {}
8+ },
9+ "required": ["foo"]
10+ },
11+ "tests": [
12+ {
13+ "description": "present required property is valid",
14+ "data": {"foo": 1},
15+ "valid": true
16+ },
17+ {
18+ "description": "non-present required property is invalid",
19+ "data": {"bar": 1},
20+ "valid": false
21+ },
22+ {
23+ "description": "ignores non-objects",
24+ "data": 12,
25+ "valid": true
26+ }
27+ ]
28+ },
29+ {
30+ "description": "required default validation",
31+ "schema": {
32+ "properties": {
33+ "foo": {}
34+ }
35+ },
36+ "tests": [
37+ {
38+ "description": "not required by default",
39+ "data": {},
40+ "valid": true
41+ }
42+ ]
43+ },
44+ {
45+ "description": "required with empty array",
46+ "schema": {
47+ "properties": {
48+ "foo": {}
49+ },
50+ "required": []
51+ },
52+ "tests": [
53+ {
54+ "description": "property not required",
55+ "data": {},
56+ "valid": true
57+ }
58+ ]
59+ }
60+]
JSON-Schema-Test-Suite/tests/draft6/type.jsonnew file+345
@@ -0,0 +1,345 @@
1+[
2+ {
3+ "description": "integer type matches integers",
4+ "schema": {"type": "integer"},
5+ "tests": [
6+ {
7+ "description": "an integer is an integer",
8+ "data": 1,
9+ "valid": true
10+ },
11+ {
12+ "description": "a float is not an integer",
13+ "data": 1.1,
14+ "valid": false
15+ },
16+ {
17+ "description": "a string is not an integer",
18+ "data": "foo",
19+ "valid": false
20+ },
21+ {
22+ "description": "a string is still not an integer, even if it looks like one",
23+ "data": "1",
24+ "valid": false
25+ },
26+ {
27+ "description": "an object is not an integer",
28+ "data": {},
29+ "valid": false
30+ },
31+ {
32+ "description": "an array is not an integer",
33+ "data": [],
34+ "valid": false
35+ },
36+ {
37+ "description": "a boolean is not an integer",
38+ "data": true,
39+ "valid": false
40+ },
41+ {
42+ "description": "null is not an integer",
43+ "data": null,
44+ "valid": false
45+ }
46+ ]
47+ },
48+ {
49+ "description": "number type matches numbers",
50+ "schema": {"type": "number"},
51+ "tests": [
52+ {
53+ "description": "an integer is a number",
54+ "data": 1,
55+ "valid": true
56+ },
57+ {
58+ "description": "a float is a number",
59+ "data": 1.1,
60+ "valid": true
61+ },
62+ {
63+ "description": "a string is not a number",
64+ "data": "foo",
65+ "valid": false
66+ },
67+ {
68+ "description": "a string is still not a number, even if it looks like one",
69+ "data": "1",
70+ "valid": false
71+ },
72+ {
73+ "description": "an object is not a number",
74+ "data": {},
75+ "valid": false
76+ },
77+ {
78+ "description": "an array is not a number",
79+ "data": [],
80+ "valid": false
81+ },
82+ {
83+ "description": "a boolean is not a number",
84+ "data": true,
85+ "valid": false
86+ },
87+ {
88+ "description": "null is not a number",
89+ "data": null,
90+ "valid": false
91+ }
92+ ]
93+ },
94+ {
95+ "description": "string type matches strings",
96+ "schema": {"type": "string"},
97+ "tests": [
98+ {
99+ "description": "1 is not a string",
100+ "data": 1,
101+ "valid": false
102+ },
103+ {
104+ "description": "a float is not a string",
105+ "data": 1.1,
106+ "valid": false
107+ },
108+ {
109+ "description": "a string is a string",
110+ "data": "foo",
111+ "valid": true
112+ },
113+ {
114+ "description": "a string is still a string, even if it looks like a number",
115+ "data": "1",
116+ "valid": true
117+ },
118+ {
119+ "description": "an object is not a string",
120+ "data": {},
121+ "valid": false
122+ },
123+ {
124+ "description": "an array is not a string",
125+ "data": [],
126+ "valid": false
127+ },
128+ {
129+ "description": "a boolean is not a string",
130+ "data": true,
131+ "valid": false
132+ },
133+ {
134+ "description": "null is not a string",
135+ "data": null,
136+ "valid": false
137+ }
138+ ]
139+ },
140+ {
141+ "description": "object type matches objects",
142+ "schema": {"type": "object"},
143+ "tests": [
144+ {
145+ "description": "an integer is not an object",
146+ "data": 1,
147+ "valid": false
148+ },
149+ {
150+ "description": "a float is not an object",
151+ "data": 1.1,
152+ "valid": false
153+ },
154+ {
155+ "description": "a string is not an object",
156+ "data": "foo",
157+ "valid": false
158+ },
159+ {
160+ "description": "an object is an object",
161+ "data": {},
162+ "valid": true
163+ },
164+ {
165+ "description": "an array is not an object",
166+ "data": [],
167+ "valid": false
168+ },
169+ {
170+ "description": "a boolean is not an object",
171+ "data": true,
172+ "valid": false
173+ },
174+ {
175+ "description": "null is not an object",
176+ "data": null,
177+ "valid": false
178+ }
179+ ]
180+ },
181+ {
182+ "description": "array type matches arrays",
183+ "schema": {"type": "array"},
184+ "tests": [
185+ {
186+ "description": "an integer is not an array",
187+ "data": 1,
188+ "valid": false
189+ },
190+ {
191+ "description": "a float is not an array",
192+ "data": 1.1,
193+ "valid": false
194+ },
195+ {
196+ "description": "a string is not an array",
197+ "data": "foo",
198+ "valid": false
199+ },
200+ {
201+ "description": "an object is not an array",
202+ "data": {},
203+ "valid": false
204+ },
205+ {
206+ "description": "an array is an array",
207+ "data": [],
208+ "valid": true
209+ },
210+ {
211+ "description": "a boolean is not an array",
212+ "data": true,
213+ "valid": false
214+ },
215+ {
216+ "description": "null is not an array",
217+ "data": null,
218+ "valid": false
219+ }
220+ ]
221+ },
222+ {
223+ "description": "boolean type matches booleans",
224+ "schema": {"type": "boolean"},
225+ "tests": [
226+ {
227+ "description": "an integer is not a boolean",
228+ "data": 1,
229+ "valid": false
230+ },
231+ {
232+ "description": "a float is not a boolean",
233+ "data": 1.1,
234+ "valid": false
235+ },
236+ {
237+ "description": "a string is not a boolean",
238+ "data": "foo",
239+ "valid": false
240+ },
241+ {
242+ "description": "an object is not a boolean",
243+ "data": {},
244+ "valid": false
245+ },
246+ {
247+ "description": "an array is not a boolean",
248+ "data": [],
249+ "valid": false
250+ },
251+ {
252+ "description": "a boolean is a boolean",
253+ "data": true,
254+ "valid": true
255+ },
256+ {
257+ "description": "null is not a boolean",
258+ "data": null,
259+ "valid": false
260+ }
261+ ]
262+ },
263+ {
264+ "description": "null type matches only the null object",
265+ "schema": {"type": "null"},
266+ "tests": [
267+ {
268+ "description": "an integer is not null",
269+ "data": 1,
270+ "valid": false
271+ },
272+ {
273+ "description": "a float is not null",
274+ "data": 1.1,
275+ "valid": false
276+ },
277+ {
278+ "description": "a string is not null",
279+ "data": "foo",
280+ "valid": false
281+ },
282+ {
283+ "description": "an object is not null",
284+ "data": {},
285+ "valid": false
286+ },
287+ {
288+ "description": "an array is not null",
289+ "data": [],
290+ "valid": false
291+ },
292+ {
293+ "description": "a boolean is not null",
294+ "data": true,
295+ "valid": false
296+ },
297+ {
298+ "description": "null is null",
299+ "data": null,
300+ "valid": true
301+ }
302+ ]
303+ },
304+ {
305+ "description": "multiple types can be specified in an array",
306+ "schema": {"type": ["integer", "string"]},
307+ "tests": [
308+ {
309+ "description": "an integer is valid",
310+ "data": 1,
311+ "valid": true
312+ },
313+ {
314+ "description": "a string is valid",
315+ "data": "foo",
316+ "valid": true
317+ },
318+ {
319+ "description": "a float is invalid",
320+ "data": 1.1,
321+ "valid": false
322+ },
323+ {
324+ "description": "an object is invalid",
325+ "data": {},
326+ "valid": false
327+ },
328+ {
329+ "description": "an array is invalid",
330+ "data": [],
331+ "valid": false
332+ },
333+ {
334+ "description": "a boolean is invalid",
335+ "data": true,
336+ "valid": false
337+ },
338+ {
339+ "description": "null is invalid",
340+ "data": null,
341+ "valid": false
342+ }
343+ ]
344+ }
345+]
JSON-Schema-Test-Suite/tests/draft6/uniqueItems.jsonnew file+79
@@ -0,0 +1,79 @@
1+[
2+ {
3+ "description": "uniqueItems validation",
4+ "schema": {"uniqueItems": true},
5+ "tests": [
6+ {
7+ "description": "unique array of integers is valid",
8+ "data": [1, 2],
9+ "valid": true
10+ },
11+ {
12+ "description": "non-unique array of integers is invalid",
13+ "data": [1, 1],
14+ "valid": false
15+ },
16+ {
17+ "description": "numbers are unique if mathematically unequal",
18+ "data": [1.0, 1.00, 1],
19+ "valid": false
20+ },
21+ {
22+ "description": "unique array of objects is valid",
23+ "data": [{"foo": "bar"}, {"foo": "baz"}],
24+ "valid": true
25+ },
26+ {
27+ "description": "non-unique array of objects is invalid",
28+ "data": [{"foo": "bar"}, {"foo": "bar"}],
29+ "valid": false
30+ },
31+ {
32+ "description": "unique array of nested objects is valid",
33+ "data": [
34+ {"foo": {"bar" : {"baz" : true}}},
35+ {"foo": {"bar" : {"baz" : false}}}
36+ ],
37+ "valid": true
38+ },
39+ {
40+ "description": "non-unique array of nested objects is invalid",
41+ "data": [
42+ {"foo": {"bar" : {"baz" : true}}},
43+ {"foo": {"bar" : {"baz" : true}}}
44+ ],
45+ "valid": false
46+ },
47+ {
48+ "description": "unique array of arrays is valid",
49+ "data": [["foo"], ["bar"]],
50+ "valid": true
51+ },
52+ {
53+ "description": "non-unique array of arrays is invalid",
54+ "data": [["foo"], ["foo"]],
55+ "valid": false
56+ },
57+ {
58+ "description": "1 and true are unique",
59+ "data": [1, true],
60+ "valid": true
61+ },
62+ {
63+ "description": "0 and false are unique",
64+ "data": [0, false],
65+ "valid": true
66+ },
67+ {
68+ "description": "unique heterogeneous types are valid",
69+ "data": [{}, [1], true, null, 1],
70+ "valid": true
71+ },
72+ {
73+ "description": "non-unique heterogeneous types are invalid",
74+ "data": [{}, [1], true, null, {}, 1],
75+ "valid": false
76+ }
77+ ]
78+ }
79+]
JSON-Schema-Test-Suite/tox.ininew file+8
@@ -0,0 +1,8 @@
1+[tox]
2+minversion = 1.6
3+envlist = py27
4+skipsdist = True
5+
6+[testenv]
7+deps = jsonschema
8+commands = {envpython} bin/jsonschema_suite check
README.md+13−49
@@ -1,61 +1,25 @@
1−# Intro
1+# DEPRECATION NOTICE
22
3−An implementation of [JSON Schema](http://json-schema.org/) Draft 4 in haskell.
3+`hjsonschema` was an attempt to build a very modular JSON Schema library. Validators have [a concrete type](src/JSONSchema/Validator/Types.hs) and can be mixed and matched into new [Specs](src/JSONSchema/Types.hs).
44
5−# Status
5+However this flexibility came at the price of complicating the code. I don't think it was the right tradeoff, especially since situations where you'd want to change what validators make up a `Spec` at runtime seem rare.
66
7−Still in development. Lacks solid code to handle changing resolution scope.
7+Also, there are many parts of JSON Schema that `hjsonschema` doesn't implement properly (as you can see from the issue tracker). I'm hoping that a new JSON Schema library will come along that handles these correctly. In the meantime I'm happy to merge working fixes into here.
88
9−# Example
10−
11−```haskell
12−{-# LANGUAGE OverloadedStrings #-}
13−
14−module Main where
15−
16−import Data.Aeson
17−import qualified Data.HashMap.Strict as H
18−import qualified Data.JsonSchema as JS
19−import qualified Data.Vector as V
20−
21−main :: IO ()
22−main = do
23− eitherGraph <- JS.fetchRefs JS.draft4 rawSchema H.empty
24− case eitherGraph of
25− Left e -> print e
26− Right graph ->
27− case JS.compileDraft4 graph rawSchema of
28− Left e2 -> print e2
29− Right schema -> print $ JS.validate schema invalidData
9+# Links
3010
31−rawSchema :: JS.RawSchema
32−rawSchema = JS.RawSchema
33− { JS._rsURI = ""
34− , JS._rsObject = H.singleton "uniqueItems" (Bool True) -- Schema JSON goes here.
35− }
11+[Hackage](https://hackage.haskell.org/package/hjsonschema) / [GitHub](https://github.com/seagreen/hjsonschema) / [Travis CI](https://travis-ci.org/seagreen/hjsonschema)
3612
37−invalidData :: Value
38−invalidData = Array (V.fromList ["foo", "foo"])
39−```
40−
41−Output:
42−```
43−fromList ["Val error against uniqueItems True for: Array (fromList [String \"foo\",String \"foo\"])"]
44−```
45−
46−
47−# Install Tests
48−
49− git submodule update --init
13+# Example
5014
51−# Notes
15+See [here](https://github.com/seagreen/hjsonschema/blob/master/examples/Simple.hs).
5216
53−+ This uses the [regexpr](https://hackage.haskell.org/package/regexpr-0.5.4) regular expression library for the "pattern" validator. I have no idea if this is compatible with the ECMA 262 regex dialect, which the [spec](http://json-schema.org/latest/json-schema-validation.html#anchor33) requires.
17+# System dependencies
5418
55−+ `draft4.json` is from commit # cc8ec81ce0abe2385ebd6c2a6f2d6deb646f874a [here](https://github.com/json-schema/json-schema).
19++ Requires [pcre](http://www.pcre.org/) (`pkgs.pcre` in Nixpkgs).
5620
57−# Credits
21+## Vendoring
5822
59−Thanks to [Julian Berman](https://github.com/Julian) for the fantastic test suite.
23++ `JSON-Schema-Test-Suite` is vendored from commit # c1b12bf699f29a04b4286711c6e3bbfba66f21e5 [here](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
6024
61−Also thanks to Tim Baumann for his [aeson-schema](https://hackage.haskell.org/package/aeson-schema) library. Hjsonschema's test code and its implementation of `Graph` both come from Aeson-Schema.
25++ `src/draft4.json` is from commit # c1b12bf699f29a04b4286711c6e3bbfba66f21e5 [here](https://github.com/json-schema/json-schema). The [root ref in remote ref](./JSON-Schema-Test-Suite/tests/draft4/refRemote.json) test has been modified to fix [#175](https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues/175).
changelog.mdnew file+209
@@ -0,0 +1,209 @@
1+# 1.8.0
2+
3++ Add GHC 8.4 support (thanks @4e6 !).
4++ Drop GHC 7.10 support.
5++ Fix JSON Pointer resolution error.
6+
7+# 1.8.0
8+
9++ Allow HTTPS references (thanks @creichert!).
10++ Use `safe-exceptions` to eliminate the risk of catching asynchronous exceptions.
11+
12+# 1.7.2
13+
14++ Remove upper bounds.
15+
16+# 1.7.1
17+
18++ Bump http-types.
19+
20+# 1.7.0
21+
22++ Test with GHC 8.2. Drop GHC 7.8.
23++ Rework `allUniqueValues` related utility functions.
24+
25+# 1.6.3
26+
27++ Bump hjsonpointer and QuickCheck.
28+
29+# 1.6.2
30+
31++ Bump aeson.
32+
33+# 1.6.1
34+
35++ Fix Haddocks.
36+
37+# 1.6.0
38+
39++ Fix defect where validators alongside "$ref" weren't ignored.
40+
41++ Fix defect where local references would fail if an "id" key had set resolution scope to start from a different document.
42+
43++ Vendor latest tests.
44+
45++ Remove `ReferencedSchemas`.
46+
47++ Create `Scope` to hold information that changes during validation.
48+
49++ Use a sum type for "type" values. Thanks to Philip Weaver (GitHub @pheaver).
50+
51+# 1.5.0.1
52+
53++ Raise test dep upper bounds.
54+
55+# 1.5.0.0
56+
57++ Report details of `"required"` validator failure.
58++ Bump `directory`.
59+
60+# 1.4.0.0
61+
62++ Rename `Data.JsonSchema.*` modules to `JSONSchema.*`.
63++ Rename `Data.Validator.*` modules to `JSONSchema.Validator.*`.
64+
65+# 1.3.0.1
66+
67++ Bump `vector`.
68+
69+# 1.3.0.0
70+
71++ Rewrite failure messages.
72+
73+We used to return a list of every failed validator, no matter how far it was buried within subschemas of the original schema.
74+
75+Now we return a tree of failure messages, so that if the first schema had only three validators, then no more than three top level failures will be returned.
76+
77++ Move the code to parse each validator from JSON from `Data.Validator.Draft4` into the validators' modules themselves.
78+
79++ Switch from Prelude to Protolude.
80+
81++ Switch `Data.JsonSchema.Fetch` from lazy to strict bytestrings.
82+
83+# 1.2.0.2
84+
85++ Bump hspec.
86+
87+# 1.2.0.1
88+
89++ Switch to hspec for tests.
90+
91+# 1.2.0.0
92+
93++ Return `AdditionalPropertiesObject` error correctly (was mistakenly returning `AdditionalItemsObject` instead.
94++ Don't silence errors resulting from subschemas of "anyOf" or "oneOf".
95+
96+# 1.1.0.1
97+
98++ Bump `aeson` and `hjsonpointer`.
99+
100+# 1.1.0.0
101+
102++ Rename `schemaForSchemas` to `metaSchema` and `schemaForSchemasBytes` to `metaSchemaBytes`.
103+
104+# 1.0.0.0
105+
106+## Bug fixes:
107+
108++ Fix JSON Pointer bug. Pointers were being built in reverse order and so were totally invalid.
109++ Use `.:!` instead of `.:?` to parse the draft 4 schema. The only way to omit optional fields in JSON Schema Draft 4 is to omit them entirely, `"null"` can't be used for this.
110+
111+## API Changes:
112+
113++ Add referenced schema loop detection.
114++ Add a new `referencesValidity` function.
115++ `checkSchema` now checks referenced schema's validity in addition to the starting schema's validity. This change bubbles up to the one-step validation functions as well.
116++ Switch most of the fetching code to use `URISchemaMap` instead of `ReferencedSchemas`. It didn't need to know about the more complicated data type.
117++ Rething failure related names. Change `Invalid` to `Failure`, add a new `Invalid` type alias which is only used for final results.
118++ Failures now include the failing part of the data as well as a JSON Pointer to it, so you don't have to worry about resolving the pointer.
119+
120+## Fundamental Changes:
121+
122++ Make `Fail` (previously `Failure`) an instance of `Functor`.
123++ Add a `Validator` data type which is an instance of `Profunctor`.
124++ Add a `Spec` data type for collections of `Validators`.
125+
126+## General:
127+
128++ Switch from 2 to 4 space indentation.
129++ Update the vendored JSON Schema Test Suite.
130+
131+# 0.10.0.3
132+
133++ Bump http-client.
134+
135+# 0.10.0.2
136+
137++ Enable GHC 8.
138+
139+# 0.10.0.1
140+
141++ Fix .cabal file.
142+
143+# 0.10
144+
145++ Rewrite fetching internals.
146++ Fix reference resolution defects, add more tests.
147++ Switch to a Perl style regex library, which is closer to ECMAScript regexes than the previous Posix style one.
148++ Add one-step validation functions ('fetchFilesystemAndValidate' and 'fetchHTTPAndValidate').
149++ Alias the validation failure type exported by 'Data.JsonSchema.Draft4' to
150+'Invalid', change its field names.
151+
152+# 0.9
153+
154++ Partial rewrite. The API of the library has changed, see the examples
155+folder for how to use the new one.
156+
157++ Users of the library can now write schemas in Haskell as well as JSON.
158+
159+# 0.8
160+
161++ Improve scope updating and resolving.
162++ Rename RawSchema's _rsObject field to _rsData.
163++ Make RawSchema's _rsURI field a Maybe. This way schemas without a starting URI can say so explicitly with Nothing instead of with "".
164++ Rename Graph to SchemaGraph. Declare it with data instead of type. Give it a field referencing the starting schema. This field is used to find the curent schema if no URI is in scope and a self-referencing $ref is found (e.g. "#").
165++ Change the order of the last two arguments to fetchReferencedSchemas.
166+
167+# 0.7.1
168+
169++ Support GHC 7.8 again.
170+
171+# 0.7
172+
173+Change error type from Text to ValidationFailure.
174+
175+Revert the 0.6 changes to validate. Also switch from Vector to list. Validate is now:
176+ Schema err -> Value -> [ValidationFailure err]
177+
178+Add fetchReferencedSchemas', which lets the user provide their own MonadIO function to be used when fetching schemas. This lets them do things like only fetch schemas from particular domains.
179+
180+# 0.6
181+
182+Break the API so the library doesn't induce boolean blindness.
183+
184+Change validate
185+ was: Schema -> Value -> Vector ValErr
186+ now: Schema -> Value -> Either (Vector ValErr) Value
187+
188+Change Schema
189+ was: type Schema = Vector Validator
190+ now: newtype Schema = Schema { _unSchema :: [Validator] }
191+
192+# 0.5.3
193+
194++ Switch from http-conduit to http-client.
195+
196+# 0.5.2
197+
198++ Add convenience function for validating and compiling draft 4 schemas simultaneously.
199+
200+# 0.5.1
201+
202++ Switch from wreq to http-conduit; drop lens dependency.
203+
204+# 0.5
205+
206++ Start changelog.
207++ Rename Utils.hs to Helpers.hs.
208++ Move all non-ValidatorGen functions in Validators.hs to Helpers.hs.
209++ Various touchups.
changelog.txtdeleted−19
@@ -1,19 +0,0 @@
1−# 0.5.3
2−
3−+ Switch from http-conduit to http-client.
4−
5−# 0.5.2
6−
7−+ Add convenience function for validating and compiling draft 4 schemas
8−simultaneously.
9−
10−# 0.5.1
11−
12−+ Switch from wreq to http-conduit; drop lens dependency.
13−
14−# 0.5
15−
16−+ Start changelog.
17−+ Rename Utils.hs to Helpers.hs.
18−+ Move all non-ValidatorGen functions in Validators.hs to Helpers.hs.
19−+ Various touchups.
examples/AlternateSchema.hsnew file+200
@@ -0,0 +1,200 @@
1+-- | An implementation of JSON Schema Draft 4 based on 'HashMap Text Value'
2+-- instead of a custom record type like 'JSONSchema.Draft4'.
3+--
4+-- If you're writing code for a new schema specification you probably want
5+-- to copy this module instead of the 'JSONSchema.Draft4'. While it's
6+-- less convenient to write schemas in Haskell without a record type, you
7+-- can get the implementation finished with far fewer lines of code.
8+module AlternateSchema where
9+
10+import Protolude
11+
12+import Data.Aeson (FromJSON(..), Value(..), decodeStrict)
13+import qualified Data.Aeson as AE
14+import qualified Data.HashMap.Strict as HM
15+import Data.Maybe (fromMaybe)
16+import Data.Profunctor (Profunctor(..))
17+
18+import JSONSchema.Draft4 (ValidatorFailure(..), metaSchemaBytes)
19+import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
20+import qualified JSONSchema.Fetch as FE
21+import JSONSchema.Types (Schema(..), Spec(..))
22+import qualified JSONSchema.Types as JT
23+import qualified JSONSchema.Validator.Draft4 as D4
24+import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
25+ updateResolutionScope)
26+
27+--------------------------------------------------
28+-- * Basic fetching tools
29+--------------------------------------------------
30+
31+referencesViaHTTP
32+ :: SchemaWithURI Schema
33+ -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))
34+referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
35+
36+draft4FetchInfo :: FE.FetchInfo Schema
37+draft4FetchInfo = FE.FetchInfo embedded (lookup "id") (lookup "$ref")
38+ where
39+ lookup :: Text -> Schema -> Maybe Text
40+ lookup k (Schema s) =
41+ case HM.lookup k s of
42+ Just (String t) -> Just t
43+ _ -> Nothing
44+
45+-- | An implementation of 'JT.embedded'.
46+embedded :: Schema -> ([Schema], [Schema])
47+embedded s =
48+ JT.embedded (d4Spec mempty mempty (Scope s Nothing (BaseURI Nothing))) s
49+
50+--------------------------------------------------
51+-- * Main API
52+--------------------------------------------------
53+
54+validate
55+ :: URISchemaMap Schema
56+ -> SchemaWithURI Schema
57+ -> Value
58+ -> [ValidatorFailure]
59+validate schemaMap sw =
60+ JT.validate (d4Spec schemaMap visited scope) (_swSchema sw)
61+ where
62+ visited :: D4.VisitedSchemas
63+ visited = D4.VisitedSchemas [(Nothing, Nothing)]
64+
65+ schemaId :: Maybe Text
66+ schemaId = FE._fiId draft4FetchInfo (_swSchema sw)
67+
68+ scope :: Scope Schema
69+ scope = Scope
70+ { _topLevelDocument = _swSchema sw
71+ , _documentURI = _swURI sw
72+ , _currentBaseURI = updateResolutionScope (BaseURI (_swURI sw))
73+ schemaId
74+ }
75+
76+-- | A schema for schemas themselves. Uses @src/draft4.json@ which is loaded
77+-- at compile time.
78+metaSchema :: Schema
79+metaSchema =
80+ fromMaybe (panic "Schema decode failed (this should never happen)")
81+ . decodeStrict
82+ $ metaSchemaBytes
83+
84+checkSchema :: Schema -> [ValidatorFailure]
85+checkSchema = validate schemaMap (SchemaWithURI metaSchema Nothing)
86+ . Object
87+ . _unSchema
88+ where
89+ schemaMap :: URISchemaMap Schema
90+ schemaMap =
91+ URISchemaMap (HM.singleton "http://json-schema.org/draft-04/schema"
92+ metaSchema)
93+
94+--------------------------------------------------
95+-- * Spec
96+--------------------------------------------------
97+
98+validateSubschema
99+ :: URISchemaMap Schema
100+ -> D4.VisitedSchemas
101+ -> Scope Schema
102+ -> Schema
103+ -> Value
104+ -> [ValidatorFailure]
105+validateSubschema schemaMap visited scope schema =
106+ JT.validate (d4Spec schemaMap visited newScope) schema
107+ where
108+ schemaId :: Maybe Text
109+ schemaId = FE._fiId draft4FetchInfo schema
110+
111+ newScope :: Scope Schema
112+ newScope = scope
113+ { _currentBaseURI = updateResolutionScope (_currentBaseURI scope)
114+ schemaId
115+ }
116+
117+d4Spec
118+ :: URISchemaMap Schema
119+ -> D4.VisitedSchemas
120+ -> Scope Schema
121+ -> Spec Schema ValidatorFailure
122+ -- ^ Here we reuses 'ValidatorFailure' from
123+ -- 'JSONSchema.Draft4.Failure'. If your validators have different
124+ -- failure possibilities you'll need to create your own validator
125+ -- failure type.
126+d4Spec schemaMap visited scope =
127+ Spec $
128+ [ dimap
129+ f
130+ FailureRef
131+ (D4.refValidator (FE.getReference schemaMap) updateScope
132+ valRef visited scope)
133+ ]
134+
135+ <> fmap (lmap disableIfRefPresent)
136+ [ dimap f FailureMultipleOf D4.multipleOfValidator
137+ , dimap f FailureMaximum D4.maximumValidator
138+ , dimap f FailureMinimum D4.minimumValidator
139+
140+ , dimap f FailureMaxLength D4.maxLengthValidator
141+ , dimap f FailureMinLength D4.minLengthValidator
142+ , dimap f FailurePattern D4.patternValidator
143+
144+ , dimap f FailureMaxItems D4.maxItemsValidator
145+ , dimap f FailureMinItems D4.minItemsValidator
146+ , dimap f FailureUniqueItems D4.uniqueItemsValidator
147+ , dimap
148+ (fromMaybe D4.emptyItems . f)
149+ (\err -> case err of
150+ D4.IRInvalidItems e -> FailureItems e
151+ D4.IRInvalidAdditional e -> FailureAdditionalItems e)
152+ (D4.itemsRelatedValidator descend)
153+ , lmap f D4.definitionsEmbedded
154+
155+ , dimap f FailureMaxProperties D4.maxPropertiesValidator
156+ , dimap f FailureMinProperties D4.minPropertiesValidator
157+ , dimap f FailureRequired D4.requiredValidator
158+ , dimap f FailureDependencies (D4.dependenciesValidator descend)
159+ , dimap
160+ (fromMaybe D4.emptyProperties . f)
161+ FailurePropertiesRelated
162+ (D4.propertiesRelatedValidator descend)
163+
164+ , dimap f FailureEnum D4.enumValidator
165+ , dimap f FailureType D4.typeValidator
166+ , dimap f FailureAllOf (D4.allOfValidator lateral)
167+ , dimap f FailureAnyOf (D4.anyOfValidator lateral)
168+ , dimap f FailureOneOf (D4.oneOfValidator lateral)
169+ , dimap f FailureNot (D4.notValidator lateral)
170+ ]
171+ where
172+ f :: FromJSON a => Schema -> Maybe a
173+ f (Schema a) =
174+ case AE.fromJSON (Object a) of
175+ AE.Error _ -> Nothing
176+ AE.Success b -> Just b
177+
178+ disableIfRefPresent :: Schema -> Schema
179+ disableIfRefPresent schema =
180+ case FE._fiRef draft4FetchInfo schema of
181+ Nothing -> schema
182+ Just _ -> Schema mempty
183+
184+ updateScope :: BaseURI -> Schema -> BaseURI
185+ updateScope uri schema =
186+ updateResolutionScope uri (FE._fiId draft4FetchInfo schema)
187+
188+ valRef
189+ :: D4.VisitedSchemas
190+ -> Scope Schema
191+ -> Schema
192+ -> Value
193+ -> [ValidatorFailure]
194+ valRef vis sc = JT.validate (d4Spec schemaMap vis sc)
195+
196+ descend :: Schema -> Value -> [ValidatorFailure]
197+ descend = validateSubschema schemaMap mempty scope
198+
199+ lateral :: Schema -> Value -> [ValidatorFailure]
200+ lateral = validateSubschema schemaMap visited scope
examples/Simple.hsnew file+37
@@ -0,0 +1,37 @@
1+module Simple where
2+
3+import Protolude
4+
5+import Data.Aeson (Value(..), decodeStrict, toJSON)
6+import qualified Data.ByteString as BS
7+import qualified Data.List.NonEmpty as NE
8+import Data.Maybe (fromMaybe)
9+
10+import qualified JSONSchema.Draft4 as D4
11+
12+badData :: Value
13+badData = toJSON [True, True]
14+
15+example :: IO ()
16+example = do
17+ bts <- BS.readFile "./examples/json/unique.json"
18+ let schema = fromMaybe (panic "Invalid schema JSON.") (decodeStrict bts)
19+ schemaWithURI = D4.SchemaWithURI
20+ schema
21+ Nothing -- This would be the URI of the schema
22+ -- if it had one. It's used if the schema
23+ -- has relative references to other
24+ -- schemas.
25+
26+ -- Fetch any referenced schemas over HTTP, check that our original schema
27+ -- itself is valid, and validate the data.
28+ res <- D4.fetchHTTPAndValidate schemaWithURI badData
29+ case res of
30+ Right () -> panic "We validated bad data."
31+ Left (D4.HVRequest _) -> panic ("Error fetching a referenced schema"
32+ <> " (either during IO or parsing).")
33+ Left (D4.HVSchema _) -> panic "Our 'schema' itself was invalid."
34+ Left (D4.HVData (D4.Invalid _ _ failures)) ->
35+ case NE.toList failures of
36+ [D4.FailureUniqueItems _] -> pure () -- Success.
37+ _ -> panic "We got a different failure than expected."
examples/TwoStep.hsnew file+48
@@ -0,0 +1,48 @@
1+-- | Differences from @examples/Simple.hs@:
2+--
3+-- * We demonstrate how to do the validation step without IO by first
4+-- getting the references with 'D4.referencesViaFilesystem'.
5+--
6+-- * This shows how to write the starting schema in Haskell instead
7+-- of parsing it from JSON.
8+--
9+-- * This schema references other schemas. Previously we used
10+-- 'fetchHTTPAndValidate' which fetched references over HTTP (though it
11+-- didn't matter because the last schema didn't actually have any).
12+-- This time we need to get them from the filesystem.
13+module TwoStep where
14+
15+import Protolude
16+
17+import Data.Aeson (Value(..), toJSON)
18+import qualified Data.List.NonEmpty as NE
19+
20+import qualified JSONSchema.Draft4 as D4
21+import qualified JSONSchema.Validator.Draft4 as VAL
22+
23+schema :: D4.Schema
24+schema = D4.emptySchema { D4._schemaRef = Just "./unique.json" }
25+
26+schemaContext :: D4.SchemaWithURI D4.Schema
27+schemaContext = D4.SchemaWithURI
28+ { D4._swSchema = schema
29+ , D4._swURI = Just "./examples/json/start.json"
30+ }
31+
32+badData :: Value
33+badData = toJSON [True, True]
34+
35+example :: IO ()
36+example = do
37+ res <- D4.referencesViaFilesystem schemaContext
38+ let references = case res of
39+ Left _ -> panic "Couldn't fetch referenced schemas."
40+ Right a -> a
41+ validate = case D4.checkSchema references schemaContext of
42+ Left _ -> panic "Not a valid schema."
43+ Right f -> f
44+ case validate badData of
45+ [] -> panic "We validated bad data."
46+ [D4.FailureRef (VAL.RefInvalid _ _ (D4.FailureUniqueItems _ NE.:|[])) ] ->
47+ pure () -- Success
48+ _ -> panic "We got a different failure than expected."
examples/json/start.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "$ref": "./unique.json"
3+}
examples/json/unique.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "uniqueItems": true
3+}
hjsonschema.cabal+155−84
@@ -1,91 +1,162 @@
1−name: hjsonschema
2−version: 0.5.3.1
3−synopsis: JSON Schema Draft 4 library
4−description: Check your Aeson structures against json-schema descriptions
5−homepage: https://github.com/seagreen/hjsonschema
6−license: MIT
7−license-file: MIT-LICENSE.txt
8−author: Ian Grant Jeffries
9−maintainer: ian@housejeffries.com
10−category: Data
11−build-type: Simple
12−cabal-version: >=1.10
13−extra-source-files: changelog.txt
14− draft4.json
15− JSON-Schema-Test-Suite/tests/draft4/*.json
16− README.md
17− tests/Lib.hs
1+name: hjsonschema
2+version: 1.9.0
3+synopsis: JSON Schema library
4+homepage: https://github.com/seagreen/hjsonschema
5+license: MIT
6+license-file: MIT-LICENSE.txt
7+author: Ian Grant Jeffries
8+maintainer: ian@housejeffries.com
9+category: Data
10+build-type: Simple
11+cabal-version: >=1.10
12+-- Rerun multi-ghc-travis (executable make-travis-yml-2) after changing:
13+Tested-With: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.1
14+extra-source-files:
15+ changelog.md
16+ JSON-Schema-Test-Suite/remotes/*.json
17+ JSON-Schema-Test-Suite/remotes/folder/*.json
18+ JSON-Schema-Test-Suite/tests/draft4/*.json
19+ JSON-Schema-Test-Suite/tests/draft4/optional/*.json
20+ README.md
21+ src/draft4.json
22+ test/Local/*.json
23+ test/supplement/*.json
24+ examples/json/*.json
1825
1926library
20− hs-source-dirs: src
21− exposed-modules: Data.JsonSchema
22− , Data.JsonSchema.Helpers
23− , Data.JsonSchema.Reference
24− , Data.JsonSchema.Validators
25− other-modules: Data.JsonSchema.Core
26− default-language: Haskell2010
27− default-extensions: OverloadedStrings
28− other-extensions: TemplateHaskell
29− ghc-options: -Wall
30− build-depends: aeson >= 0.7 && < 0.9
31− , base >= 4.6 && < 4.9
32− , bytestring >= 0.10 && < 0.11
33− , file-embed >= 0.0.8 && < 0.0.9
34− , hashable >= 1.2 && < 1.3
35− , hjsonpointer >= 0.2 && < 0.3
36− , http-client >= 0.4.9 && < 0.5
37− , http-types >= 0.8 && < 0.9
38− , regexpr >= 0.5 && < 0.6
39− , scientific >= 0.3 && < 0.4
40− , unordered-containers >= 0.2 && < 0.3
41− , text >= 1.2 && < 1.3
42− , vector >= 0.10 && < 0.11
27+ hs-source-dirs:
28+ src
29+ default-language: Haskell2010
30+ default-extensions:
31+ DeriveFunctor
32+ DeriveGeneric
33+ GeneralizedNewtypeDeriving
34+ NoImplicitPrelude
35+ OverloadedStrings
36+ ScopedTypeVariables
37+ if impl(ghc >= 8)
38+ default-extensions: StrictData
39+ ghc-options:
40+ -Wall
41+ exposed-modules:
42+ JSONSchema.Draft4
43+ , JSONSchema.Draft4.Failure
44+ , JSONSchema.Draft4.Schema
45+ , JSONSchema.Draft4.Spec
46+ , JSONSchema.Fetch
47+ , JSONSchema.Types
48+ , JSONSchema.Validator.Draft4
49+ , JSONSchema.Validator.Draft4.Any
50+ , JSONSchema.Validator.Draft4.Array
51+ , JSONSchema.Validator.Draft4.Number
52+ , JSONSchema.Validator.Draft4.Object
53+ , JSONSchema.Validator.Draft4.String
54+ , JSONSchema.Validator.Reference
55+ , JSONSchema.Validator.Types
56+ , JSONSchema.Validator.Utils
57+ other-modules:
58+ JSONSchema.Validator.Draft4.Object.Properties
59+ , Import
60+ build-depends:
61+ base >= 4.7 && < 5
62+ -- 0.11 is for `.:!`:
63+ , aeson >= 0.11
64+ , bytestring >= 0.10
65+ , containers >= 0.5
66+ , file-embed >= 0.0.8
67+ , filepath >= 1.3
68+ , hashable >= 1.2
69+ , hjsonpointer >= 1.1
70+ -- 0.4.30 is for parseUrlThrow:
71+ , http-client >= 0.4.30
72+ , http-client-tls >= 0.3
73+ , http-types >= 0.8
74+ , pcre-heavy >= 1.0
75+ , profunctors >= 5.0
76+ , protolude >= 0.1.10
77+ , QuickCheck >= 2.8
78+ , safe-exceptions >= 0.1.6
79+ , scientific >= 0.3
80+ , unordered-containers >= 0.2
81+ , text >= 1.1
82+ , vector >= 0.10
4383
4484test-suite local
45− type: exitcode-stdio-1.0
46− hs-source-dirs: tests
47− main-is: Local.hs
48− default-language: Haskell2010
49− ghc-options: -Wall
50− default-extensions: OverloadedStrings
51− other-extensions: TemplateHaskell
52− build-depends: aeson
53− , base
54− , bytestring
55− , hjsonschema
56− , unordered-containers
57− , text
58− , vector
59− , directory >= 1.2 && < 1.3
60− , filepath >= 1.3 && < 1.4
61− , HUnit >= 1.2 && < 1.3
62− , test-framework >= 0.8 && < 0.9
63− , test-framework-hunit >= 0.3 && < 0.4
85+ hs-source-dirs:
86+ test
87+ examples
88+ main-is: Local.hs
89+ type: exitcode-stdio-1.0
90+ default-language: Haskell2010
91+ ghc-options:
92+ -Wall
93+ -fno-warn-orphans
94+ default-extensions:
95+ DeriveGeneric
96+ NoImplicitPrelude
97+ OverloadedStrings
98+ ScopedTypeVariables
99+ other-modules:
100+ Local.Failure
101+ , Local.Reference
102+ , Local.Validation
103+ , Shared
104+ -- from ./examples:
105+ , AlternateSchema
106+ , Simple
107+ , TwoStep
108+ build-depends:
109+ base
110+ , aeson
111+ , bytestring
112+ , filepath
113+ , hjsonpointer
114+ , hjsonschema
115+ , profunctors
116+ , protolude
117+ , text
118+ , QuickCheck
119+ , unordered-containers
120+ , vector
121+
122+ -- directory-1.2.5 required for `listDirectory`:
123+ , directory >= 1.2.5
124+ , hspec >= 2.2
64125
65126test-suite remote
66− type: exitcode-stdio-1.0
67− hs-source-dirs: tests
68− main-is: Remote.hs
69− default-language: Haskell2010
70− ghc-options: -Wall
71− default-extensions: OverloadedStrings
72− other-extensions: TemplateHaskell
73− build-depends: aeson
74− , async
75− , base
76− , bytestring
77− , hjsonschema
78− , unordered-containers
79− , text
80− , vector
81− , directory >= 1.2 && < 1.3
82− , filepath >= 1.3 && < 1.4
83− , HUnit >= 1.2 && < 1.3
84− , test-framework >= 0.8 && < 0.9
85− , test-framework-hunit >= 0.3 && < 0.4
86− , wai-app-static
87− , warp
127+ hs-source-dirs:
128+ test
129+ examples
130+ main-is: Remote.hs
131+ type: exitcode-stdio-1.0
132+ default-language: Haskell2010
133+ ghc-options:
134+ -Wall
135+ -fno-warn-orphans
136+ default-extensions:
137+ DeriveGeneric
138+ NoImplicitPrelude
139+ OverloadedStrings
140+ ScopedTypeVariables
141+ other-modules:
142+ Shared
143+ -- from ./examples:
144+ , AlternateSchema
145+ build-depends:
146+ base
147+ , aeson
148+ , async
149+ , bytestring
150+ , filepath
151+ , hjsonpointer
152+ , hjsonschema
153+ , profunctors
154+ , protolude
155+ , text
156+ , unordered-containers
157+ , vector
88158
89−source-repository head
90− type: git
91− location: git://github.com/seagreen/hjsonschema.git
159+ , directory
160+ , hspec
161+ , wai-app-static
162+ , warp
shell.nixnew file+15
@@ -0,0 +1,15 @@
1+{ghc}:
2+with (import <nixpkgs> {});
3+
4+haskell.lib.buildStackProject {
5+ name = "none";
6+ inherit ghc;
7+ buildInputs = [
8+ pcre
9+
10+ git # for `git:` references in stack.yml.
11+ ncurses # for intero
12+ pkgconfig
13+ zlib
14+ ];
15+}
src/Data/JsonSchema.hsdeleted−127
@@ -1,127 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Data.JsonSchema
4− ( module Data.JsonSchema.Core
5− , module Data.JsonSchema
6− ) where
7−
8−import Control.Applicative
9−import Data.Aeson
10−import Data.ByteString.Lazy (fromStrict)
11−import Data.FileEmbed
12−import Data.Foldable
13−import qualified Data.HashMap.Strict as H
14−import Data.JsonSchema.Core
15−import Data.JsonSchema.Helpers
16−import Data.JsonSchema.Reference
17−import Data.JsonSchema.Validators
18−import Data.Maybe
19−import Data.Text (Text)
20−import qualified Data.Text as T
21−import Data.Vector (Vector)
22−import qualified Data.Vector as V
23−import Prelude hiding (foldr)
24−
25−--------------------------------------------------
26−-- * Draft 4 Specific
27−--------------------------------------------------
28−
29−draft4 :: Spec
30−draft4 = Spec $ H.fromList
31− [ ("$ref" , (ref , noEm))
32− , ("multipleOf" , (multipleOf , noEm))
33− , ("maximum" , (maximumVal , noEm))
34− , ("minimum" , (minimumVal , noEm))
35− , ("maxLength" , (maxLength , noEm))
36− , ("minLength" , (minLength , noEm))
37− , ("pattern" , (pattern , noEm))
38− , ("additionalItems" , (noVal , objEmbed))
39− , ("items" , (items , objOrArrayEmbed))
40− , ("maxItems" , (maxItems , noEm))
41− , ("minItems" , (minItems , noEm))
42− , ("uniqueItems" , (uniqueItems , noEm))
43− , ("maxProperties" , (maxProperties , noEm))
44− , ("minProperties" , (minProperties , noEm))
45− , ("required" , (required , noEm))
46− , ("properties" , (properties , objMembersEmbed))
47− , ("patternProperties" , (patternProperties , objMembersEmbed))
48− , ("additionalProperties", (additionalProperties, objEmbed))
49− , ("dependencies" , (dependencies , objMembersEmbed))
50− , ("enum" , (enum , noEm))
51− , ("type" , (typeVal , noEm))
52− , ("allOf" , (allOf , arrayEmbed))
53− , ("anyOf" , (anyOf , arrayEmbed))
54− , ("oneOf" , (oneOf , arrayEmbed))
55− , ("not" , (notValidator , objEmbed))
56− , ("definitions" , (noVal , objMembersEmbed))
57− ]
58−
59−-- | Check if a 'RawSchema' is valid Draft 4 schema.
60−--
61−-- This is just a convenience function built by preloading 'validate'
62−-- with the spec schema that describes valid Draft 4 schemas.
63−--
64−-- NOTE: It's not actually required to run 'isValidSchema' on
65−-- prospective draft 4 schemas at all. However, it's a good way to
66−-- catch unintentional mistakes in schema documents.
67−isValidSchema :: RawSchema -> Vector ValErr
68−isValidSchema r =
69− case decode . fromStrict $ $(embedFile "draft4.json") of
70− Nothing -> V.singleton "Schema decode failed (this should never happen)"
71− Just s -> do
72− let draft4Schema = RawSchema { _rsURI = "", _rsObject = s }
73− validate
74− (compile draft4 H.empty draft4Schema)
75− (Object . _rsObject $ r)
76−
77−-- | Check that a 'RawSchema' conforms to the JSON Schema Draft 4
78−-- master schema document. Compile it if it does.
79−--
80−-- This is just a convenience function built by combining
81−-- 'isValidSchema' and 'compile'.
82−compileDraft4 :: Graph -> RawSchema -> Either (Vector ValErr) Schema
83−compileDraft4 g r =
84− let es = isValidSchema r
85− in case V.length es of
86− 0 -> Right $ compile draft4 g r
87− _ -> Left es
88−
89−--------------------------------------------------
90−-- * Graph Builder
91−--------------------------------------------------
92−-- $ TODO: It would be nice to have a few other functions for building
93−-- out 'Graph's depending on the situation. For instance, there could be
94−-- a function that only fetched "$ref"s which use the "file://" scheme.
95−
96−-- | Take a schema. Retrieve every document either it or its
97−-- subschemas include via the "$ref" keyword. Load a 'Graph' out
98−-- with them.
99−--
100−-- TODO: This function's URL processing is hacky and needs improvement.
101−fetchRefs :: Spec -> RawSchema -> Graph -> IO (Either Text Graph)
102−fetchRefs spec a graph =
103− let startingGraph = H.insert (_rsURI a) (_rsObject a) graph
104− in foldlM fetch (Right startingGraph) (includeSubschemas a)
105−
106− where
107− includeSubschemas :: RawSchema -> Vector RawSchema
108− includeSubschemas r =
109− let newId = newResolutionScope (_rsURI r) (_rsObject r)
110− xs = H.intersectionWith (\(_,f) x -> f newId x) (_unSpec spec) (_rsObject r)
111− ys = V.concat . H.elems $ xs -- TODO: optimize
112− in V.cons r . V.concat . V.toList $ includeSubschemas <$> ys
113−
114− fetch :: Either Text Graph -> RawSchema -> IO (Either Text Graph)
115− fetch (Right g) r =
116− case H.lookup "$ref" (_rsObject r) >>= toTxt >>= refAndPointer of
117− Nothing -> return (Right g)
118− Just (s, _) ->
119− let t = (_rsURI r `combineIdAndRef` s)
120− in if T.length t <= 0 || H.member t g || not ("://" `T.isInfixOf` t)
121− then return (Right g)
122− else do
123− eResp <- fetchRef t
124− case eResp of
125− Left e -> return (Left e)
126− Right obj -> fetchRefs spec (RawSchema t obj) g
127− fetch leftGraph _ = return leftGraph
src/Data/JsonSchema/Core.hsdeleted−55
@@ -1,55 +0,0 @@
1−module Data.JsonSchema.Core where
2−
3−import Data.Aeson
4−import Data.HashMap.Strict (HashMap)
5−import qualified Data.HashMap.Strict as H
6−import Data.JsonSchema.Reference
7−import Data.Maybe
8−import Data.Text (Text)
9−import Data.Vector (Vector)
10−import qualified Data.Vector as V
11−
12−--------------------------------------------------
13−-- * Primary API
14−--------------------------------------------------
15−
16−compile :: Spec -> Graph -> RawSchema -> Schema
17−compile spec g (RawSchema t o) =
18− V.fromList . catMaybes . H.elems $ H.intersectionWith f (_unSpec spec) o
19− where
20− f :: (ValidatorGen, a) -> Value -> Maybe Validator
21− f (vGen,_) = vGen spec g $ RawSchema (newResolutionScope t o) o
22−
23−validate :: Schema -> Value -> Vector ValErr
24−validate s x = s >>= ($ x)
25−
26−--------------------------------------------------
27−-- * Types
28−--------------------------------------------------
29−
30−newtype Spec = Spec
31− { _unSpec :: HashMap Text (ValidatorGen, EmbeddedSchemas)
32− }
33−
34−-- | Set of potentially mutually recursive schemas.
35−type Graph = HashMap Text (HashMap Text Value)
36−
37−type ValErr = Text
38−
39−type Validator = Value -> Vector ValErr
40−
41−type Schema = Vector Validator
42−
43−type ValidatorGen = Spec -> Graph -> RawSchema -> Value -> Maybe Validator
44−
45−-- | Return a schema's immediate subschemas.
46−--
47−-- This is used by 'Data.JsonSchema.fetchRefs' to find all the
48−-- subschemas in a document. This allows it to process only
49−-- "$ref"s and "id"s that are actual schema keywords.
50−type EmbeddedSchemas = Text -> Value -> Vector RawSchema
51−
52−data RawSchema = RawSchema
53− { _rsURI :: Text
54− , _rsObject :: HashMap Text Value
55− }
src/Data/JsonSchema/Helpers.hsdeleted−176
@@ -1,176 +0,0 @@
1−module Data.JsonSchema.Helpers where
2−
3−import Control.Applicative
4−import Control.Monad
5−import Data.Aeson
6−import Data.Hashable
7−import Data.HashMap.Strict (HashMap)
8−import qualified Data.HashMap.Strict as H
9−import Data.JsonSchema.Core
10−import Data.List
11−import Data.Monoid
12−import Data.Scientific
13−import Data.Text (Text)
14−import qualified Data.Text as T
15−import Data.Traversable
16−import Data.Vector (Vector)
17−import qualified Data.Vector as V
18−import Text.RegexPR
19−
20−--------------------------------------------------
21−-- * Embedded Schema Layouts
22−--------------------------------------------------
23−
24−noEm :: EmbeddedSchemas
25−noEm _ _ = V.empty
26−
27−objEmbed :: EmbeddedSchemas
28−objEmbed t (Object o) = V.singleton $ RawSchema t o
29−objEmbed _ _ = V.empty
30−
31−-- TODO: optimize
32−arrayEmbed :: EmbeddedSchemas
33−arrayEmbed t (Array vs) = V.concat . V.toList $ objEmbed t <$> vs
34−arrayEmbed _ _ = V.empty
35−
36−objOrArrayEmbed :: EmbeddedSchemas
37−objOrArrayEmbed t v@(Object _) = objEmbed t v
38−objOrArrayEmbed t v@(Array _) = arrayEmbed t v
39−objOrArrayEmbed _ _ = V.empty
40−
41−objMembersEmbed :: EmbeddedSchemas
42−objMembersEmbed t (Object o) = V.concat $ objEmbed t <$> H.elems o
43−objMembersEmbed _ _ = V.empty
44−
45−--------------------------------------------------
46−-- * Validator Helpers
47−--------------------------------------------------
48−
49−propertiesMatches
50− :: Spec
51− -> Graph
52− -> RawSchema
53− -> Value
54− -> Maybe (Value -> (Vector ValErr, Value))
55−propertiesMatches spec g s (Object val) = do
56− os <- traverse toObj val
57− let oss = compile spec g . RawSchema (_rsURI s) <$> os
58− Just (\x ->
59− case x of
60− Object y -> ( join . vectorOfElems $ H.intersectionWith validate oss y
61− , Object $ H.difference y oss)
62− z -> (mempty, z))
63−propertiesMatches _ _ _ _ = Nothing
64−
65−patternPropertiesMatches
66− :: Spec
67− -> Graph
68− -> RawSchema
69− -> Value
70− -> Maybe (Value -> (Vector ValErr, Value))
71−patternPropertiesMatches spec g s (Object val) = do
72− os <- traverse toObj val
73− let vs = compile spec g . RawSchema (_rsURI s) <$> os
74− Just (\x ->
75− case x of
76− Object y -> let ms = matches (hmToVector vs) <$> hmToVector y
77− in (ms >>= runVals, leftovers ms)
78− _ -> (mempty, x))
79− where
80− matches
81− :: Vector (Text, Schema)
82− -> (Text, Value)
83− -> (Text, Value, Vector Schema)
84− matches ss (k, v) = (k, v, ss >>= match k)
85−
86− match :: Text -> (Text, Schema) -> Vector Schema
87− match k (r, sc) =
88− case matchRegexPR (T.unpack r) (T.unpack k) of
89− Nothing -> mempty
90− Just _ -> V.singleton sc
91−
92− runVals :: (Text, Value, Vector Schema) -> Vector ValErr
93− runVals (_,v,ss) = join $ validate <$> ss <*> pure v
94−
95− leftovers :: Vector (Text, Value, Vector Schema) -> Value
96− leftovers possiblyMatched =
97− let unmatched = V.filter (\(_,_,ss) -> V.length ss == 0) possiblyMatched
98− in Object . vectorToHm $ (\(v,k,_) -> (v,k)) <$> unmatched
99−
100−patternPropertiesMatches _ _ _ _ = Nothing
101−
102−isJsonType :: Value -> Vector Text -> Vector ValErr
103−isJsonType x xs =
104− case x of
105− (Null) -> f "null" xs ("null" :: Text)
106− (Array y) -> f "array" xs y
107− (Bool y) -> f "boolean" xs y
108− (Object y) -> f "object" xs y
109− (String y) -> f "string" xs y
110− (Number y) ->
111− case toBoundedInteger y :: Maybe Int of
112− Nothing -> f "number" xs y
113− Just _ -> if V.elem "number" xs || V.elem "integer" xs
114− then mempty
115− else mkErr y xs
116− where
117− f :: (Show a) => Text -> Vector Text -> a -> Vector ValErr
118− f t ts d = if V.elem t ts then mempty else mkErr d ts
119−
120− mkErr :: (Show a) => a -> Vector Text -> Vector ValErr
121− mkErr y ts = V.singleton $ tshow y <> " is not one of the types " <> tshow ts
122−
123−--------------------------------------------------
124−-- * Utils
125−--------------------------------------------------
126−
127−eitherToMaybe :: Either a b -> Maybe b
128−eitherToMaybe (Left _) = Nothing
129−eitherToMaybe (Right a) = Just a
130−
131−vectorOfElems :: HashMap k a -> Vector a
132−vectorOfElems = V.fromList . H.elems
133−
134−hmToVector :: HashMap k a -> Vector (k, a)
135−hmToVector = V.fromList . H.toList
136−
137−vectorToHm :: (Eq k, Hashable k) => Vector (k, a) -> HashMap k a
138−vectorToHm = H.fromList . V.toList
139−
140−runMaybeVal :: Maybe Validator -> Value -> Vector ValErr
141−runMaybeVal Nothing _ = mempty
142−runMaybeVal (Just val) d = val d
143−
144−runMaybeVal'
145− :: Maybe (Value -> (Vector ValErr, Value))
146− -> Value
147− -> (Vector ValErr, Value)
148−runMaybeVal' Nothing d = (mempty, d)
149−runMaybeVal' (Just val) d = val d
150−
151−-- TODO: optimize
152−-- see here: http://comments.gmane.org/gmane.comp.lang.haskell.cafe/106242
153−allUnique :: (Eq a) => Vector a -> Bool
154−allUnique bs = length (nub (V.toList bs)) == V.length bs
155−
156−-- TODO: optimize
157−count :: (Eq a) => a -> Vector a -> Int
158−count b bs = V.length $ V.filter (== b) bs
159−
160−toObj :: Value -> Maybe (HashMap Text Value)
161−toObj (Object a) = Just a
162−toObj _ = Nothing
163−
164−fromJSONInt :: Value -> Maybe Int
165−fromJSONInt (Number n) = toBoundedInteger n
166−fromJSONInt _ = Nothing
167−
168−toTxt :: Value -> Maybe Text
169−toTxt (String t) = Just t
170−toTxt _ = Nothing
171−
172−greaterThanZero :: (Num a, Ord a) => a -> Maybe ()
173−greaterThanZero n = if n <= 0 then Nothing else Just ()
174−
175−tshow :: Show a => a -> Text
176−tshow = T.pack . show
src/Data/JsonSchema/Reference.hsdeleted−68
@@ -1,68 +0,0 @@
1−-- | TODO: The code handling resolution scope updates
2−-- is hacky and needs improvement.
3−
4−module Data.JsonSchema.Reference where
5−
6−import Control.Applicative
7−import Control.Arrow
8−import Control.Exception
9−import Control.Monad
10−import Data.Aeson
11−import Data.ByteString.Lazy (ByteString)
12−import Data.HashMap.Strict (HashMap)
13−import qualified Data.HashMap.Strict as H
14−import Data.Monoid
15−import Data.Text (Text)
16−import qualified Data.Text as T
17−import Network.HTTP.Client
18−import Prelude hiding (foldr)
19−
20−combineIdAndRef :: Text -> Text -> Text
21−combineIdAndRef a b
22− | "://" `T.isInfixOf` b = b
23− | T.length a < 1 || T.length b < 1 = a <> b
24− | T.last a == '#' && T.head b == '#' = a <> T.tail b
25− | otherwise = a <> b
26−
27−combineIds :: Text -> Text -> Text
28−combineIds a b
29− | b == "#" || b == "" = a
30− | "://" `T.isInfixOf` b = b
31− | T.length a < 1 || T.length b < 1 = a <> b
32− | T.last a == '#' && T.head b == '#' = a <> T.tail b
33− | otherwise = a <> b
34−
35−newResolutionScope :: Text -> HashMap Text Value -> Text
36−newResolutionScope t o =
37− case H.lookup "id" o of
38− Just (String idKeyword) -> t `combineIds` idKeyword
39− _ -> t
40−
41−refAndPointer :: Text -> Maybe (Text, Text)
42−refAndPointer val = getParts $ T.splitOn "#" val
43− where
44− getParts :: [Text] -> Maybe (Text, Text)
45− getParts [] = Just ("","")
46− getParts [x] = Just (x,"")
47− getParts [x,y] = Just (x,y)
48− getParts _ = Nothing
49−
50−fetchRef :: Text -> IO (Either Text (HashMap Text Value))
51−fetchRef t = do
52− eResp <- safeGet t
53− case eResp of
54− Left e -> return $ Left e
55− Right b -> return . left T.pack $ eitherDecode b
56−
57−safeGet :: Text -> IO (Either Text ByteString)
58−safeGet url = catch (Right <$> simpleHttp') handler
59− where
60− handler :: SomeException -> IO (Either Text ByteString)
61− handler e = return . Left . T.pack . show $ e
62−
63− -- We don't want to depend on http-conduit, but Network.Http.Conduit.simpleHttp
64− -- is the model for this function. simpleHttp also sets "Connection: close".
65− simpleHttp' :: IO ByteString
66− simpleHttp' = fmap responseBody $ withManager defaultManagerSettings $ \man -> do
67− req <- parseUrl (T.unpack url)
68− httpLbs req { requestHeaders = ("Connection", "close") : requestHeaders req } man
src/Data/JsonSchema/Validators.hsdeleted−438
@@ -1,438 +0,0 @@
1−-- | This is generally meant to be an internal module. It's only
2−-- exposed in case you want to make your own 'Spec'. If you just
3−-- want to use JSON Schema Draft 4 use the preassembled
4−-- 'Data.JsonSchema.draft4' instead.
5−
6−module Data.JsonSchema.Validators where
7−
8−import Control.Applicative
9−import Control.Monad
10−import Data.Aeson
11−import Data.Fixed (mod')
12−import Data.Hashable
13−import Data.HashMap.Strict (HashMap)
14−import qualified Data.HashMap.Strict as H
15−import Data.JsonPointer
16−import Data.JsonSchema.Core
17−import Data.JsonSchema.Helpers
18−import Data.JsonSchema.Reference
19−import Data.Maybe
20−import Data.Monoid
21−import Data.Text (Text)
22−import qualified Data.Text as T
23−import Data.Text.Encoding
24−import Data.Traversable
25−import Data.Vector (Vector)
26−import qualified Data.Vector as V
27−import Network.HTTP.Types.URI
28−import Text.RegexPR
29−
30−--------------------------------------------------
31−-- * Number Validators
32−--------------------------------------------------
33−
34−multipleOf :: ValidatorGen
35−multipleOf _ _ _ (Number val) = do
36− greaterThanZero val
37− Just (\x ->
38− case x of
39− Number y ->
40− if y `mod'` val /= 0
41− then V.singleton $ tshow y <> " isn't a multiple of " <> tshow val
42− else mempty
43− _ -> mempty)
44−multipleOf _ _ _ _ = Nothing
45−
46−maximumVal :: ValidatorGen
47−maximumVal _ _ s (Number val) =
48− let f = case H.lookup "exclusiveMaximum" (_rsObject s) of
49− Just (Bool a) -> if a then (>=) else (>)
50− _ -> (>)
51− in Just (\x ->
52− case x of
53− Number y ->
54− if y `f` val
55− then V.singleton $ tshow y <> " fails to validate against maximum " <> tshow val
56− else mempty
57− _ -> mempty)
58−maximumVal _ _ _ _ = Nothing
59−
60−minimumVal :: ValidatorGen
61−minimumVal _ _ s (Number val) =
62− let f = case H.lookup "exclusiveMinimum" (_rsObject s) of
63− Just (Bool a) -> if a then (<=) else (<)
64− _ -> (<)
65− in Just (\x ->
66− case x of
67− Number y ->
68− if y `f` val
69− then V.singleton $ tshow y <> " fails to validate against minimum " <> tshow val
70− else mempty
71− _ -> mempty)
72−minimumVal _ _ _ _ = Nothing
73−
74−--------------------------------------------------
75−-- * String Validators
76−--------------------------------------------------
77−
78−maxLength :: ValidatorGen
79−maxLength _ _ _ v = do
80− val <- fromJSONInt v
81− greaterThanZero val
82− Just (\x ->
83− case x of
84− String y ->
85− if T.length y > val
86− then V.singleton $ y <> " is greater than maxLength " <> tshow val
87− else mempty
88− _ -> mempty)
89−
90−minLength :: ValidatorGen
91−minLength _ _ _ v = do
92− val <- fromJSONInt v
93− greaterThanZero val
94− Just (\x ->
95− case x of
96− String y ->
97− if T.length y < val
98− then V.singleton $ y <> " is less than minLength " <> tshow val
99− else mempty
100− _ -> mempty)
101−
102−pattern :: ValidatorGen
103−pattern _ _ _ (String val) =
104− Just (\x ->
105− case x of
106− String t ->
107− case matchRegexPR (T.unpack val) (T.unpack t) of
108− Nothing -> V.singleton $ t <> " fails to validate against pattern " <> val
109− Just _ -> mempty
110− _ -> mempty)
111−pattern _ _ _ _ = Nothing
112−
113−--------------------------------------------------
114−-- * Array Validators
115−--------------------------------------------------
116−
117−-- | Also covers additionalItems.
118−items :: ValidatorGen
119−items spec g s (Object val) =
120− let sub = compile spec g (RawSchema (_rsURI s) val)
121− in Just (\x ->
122− case x of
123− Array ys -> ys >>= validate sub
124− _ -> mempty)
125−items spec g s (Array vs) = do
126− os <- traverse toObj vs
127− let ss = compile spec g . RawSchema (_rsURI s) <$> os
128− let addItems = do
129− a <- H.lookup "additionalItems" (_rsObject s)
130− additionalItems spec g s a
131− Just (\x ->
132− case x of
133− (Array ys) ->
134− let extras = V.drop (V.length os) ys
135− in join (V.zipWith validate ss ys) <> runMaybeVal addItems (Array extras)
136− _ -> mempty)
137−items _ _ _ _ = Nothing
138−
139−-- | Not included directly in the 'draft4' spec hashmap because it always
140−-- validates data unless 'items' is also present. This is because 'items'
141−-- defaults to {}.
142−additionalItems :: ValidatorGen
143−additionalItems _ _ _ (Bool val) =
144− Just (\x ->
145− case x of
146− Array ys ->
147− if not val && V.length ys > 0
148− then V.singleton ("Val error against additionalItems false for: " <> tshow x)
149− else mempty
150− _ -> mempty)
151−additionalItems spec g s (Object val) =
152− let sub = compile spec g (RawSchema (_rsURI s) val)
153− in Just (\x ->
154− case x of
155− Array ys -> ys >>= validate sub
156− _ -> mempty)
157−additionalItems _ _ _ _ = Nothing
158−
159−maxItems :: ValidatorGen
160−maxItems _ _ _ v = do
161− val <- fromJSONInt v
162− greaterThanZero val
163− Just (\x ->
164− case x of
165− Array ys ->
166− if V.length ys > val
167− then V.singleton $ tshow ys <> " has more items than maxItems " <> tshow val
168− else mempty
169− _ -> mempty)
170−
171−minItems :: ValidatorGen
172−minItems _ _ _ v = do
173− val <- fromJSONInt v
174− greaterThanZero val
175− Just (\x ->
176− case x of
177− Array ys ->
178− if V.length ys < val
179− then V.singleton $ tshow ys <> " has fewer items than minItems " <> tshow val
180− else mempty
181− _ -> mempty)
182−
183−uniqueItems :: ValidatorGen
184−uniqueItems _ _ _ (Bool val) = do
185− unless val Nothing
186− Just (\x ->
187− case x of
188− (Array ys) -> if allUnique ys
189− then mempty
190− else V.singleton ("Val error against uniqueItems " <> tshow val <> " for: " <> tshow x)
191− _ -> mempty)
192−uniqueItems _ _ _ _ = Nothing
193−
194−--------------------------------------------------
195−-- * Object Validators
196−--------------------------------------------------
197−
198−maxProperties :: ValidatorGen
199−maxProperties _ _ _ v = do
200− val <- fromJSONInt v
201− greaterThanZero val
202− Just (\x ->
203− case x of
204− Object o ->
205− if H.size o > val
206− then V.singleton $ tshow o <> " has more members than maxProperties " <> tshow val
207− else mempty
208− _ -> mempty)
209−
210−minProperties :: ValidatorGen
211−minProperties _ _ _ v = do
212− val <- fromJSONInt v
213− greaterThanZero val
214− Just (\x ->
215− case x of
216− Object o ->
217− if H.size o < val
218− then V.singleton $ tshow o <> " has fewer members than minProperties " <> tshow val
219− else mempty
220− _ -> mempty)
221−
222−required :: ValidatorGen
223−required _ _ _ (Array vs) = do
224− when (V.length vs == 0) Nothing
225− ts <- traverse toTxt vs
226− let a = vectorToMapSet ts
227− when (H.size a /= V.length ts) Nothing
228− Just (\x ->
229− case x of
230− Object o ->
231− if H.size (H.difference a o) > 0
232− then V.singleton $ "the keys of " <> tshow o <>
233− " don't contain all the required elements " <> tshow vs
234− else mempty
235− _ -> mempty)
236− where
237− vectorToMapSet :: (Eq a, Hashable a) => Vector a -> HashMap a Bool
238− vectorToMapSet vec = vectorToHm $ (\x -> (x, True)) <$> vec
239−required _ _ _ _ = Nothing
240−
241−-- TODO: Fix up the properties validators. They're all a huge mess, but at least
242−-- they work.
243−--
244−-- In order of what's tried: properties, patternProperties, additionalProperties
245−properties :: ValidatorGen
246−properties spec g s v = do
247− let mProps = propertiesMatches spec g s v
248− let mPatProp = do
249− aV <- H.lookup "patternProperties" (_rsObject s)
250− patternPropertiesMatches spec g s aV
251− let mAdd = do
252− aVal <- H.lookup "additionalProperties" (_rsObject s)
253− runAdditionalProperties spec g s aVal
254− when (isNothing mProps && isNothing mPatProp && isNothing mAdd) Nothing
255− Just (\x ->
256− case x of
257− Object y -> -- Got myself into a mess here.
258− let (e1s, remaining) = runMaybeVal' mProps (Object y)
259− (_, remaining') = runMaybeVal' mPatProp remaining
260− (e2s, _) = runMaybeVal' mPatProp (Object y)
261− in e1s <> e2s <> runMaybeVal mAdd remaining'
262− _ -> mempty)
263−
264−patternProperties :: ValidatorGen
265−patternProperties spec g s v = do
266− when (H.member "properties" (_rsObject s)) Nothing
267− let mPatProp = patternPropertiesMatches spec g s v
268− -- TODO: checking additionalProperties as well doesn't help with tests
269− let mAdd = do
270− aVal <- H.lookup "additionalProperties" (_rsObject s)
271− runAdditionalProperties spec g s aVal
272− when (isNothing mPatProp && isNothing mAdd) Nothing
273− Just (\x ->
274− case x of
275− Object y ->
276− let (e2s, remaining') = runMaybeVal' mPatProp (Object y)
277− in e2s <> runMaybeVal mAdd remaining'
278− _ -> mempty)
279−
280−-- | An implementation of the "additionalProperties" keyword that never
281−-- disables itself. Not included directly in the 'draft4' spec hashmap.
282−runAdditionalProperties :: ValidatorGen
283−runAdditionalProperties _ _ _ (Bool val) =
284− Just (\x ->
285− case x of
286− Object y ->
287− if not val && H.size y > 0
288− then V.singleton ("Val error against additionalProperties false for: " <> tshow x)
289− else mempty
290− _ -> mempty)
291−runAdditionalProperties spec g s (Object val) =
292− let sub = compile spec g (RawSchema (_rsURI s) val)
293− in Just (\x ->
294− case x of
295− Object y -> vectorOfElems y >>= validate sub
296− _ -> mempty)
297−runAdditionalProperties _ _ _ _ = Nothing
298−
299−additionalProperties :: ValidatorGen
300−additionalProperties spec g s v = do
301− when (H.member "properties" (_rsObject s)) Nothing
302− when (H.member "patternProperties" (_rsObject s)) Nothing
303− runAdditionalProperties spec g s v
304−
305−-- http://json-schema.org/latest/json-schema-validation.html#anchor70
306−--
307−-- This keyword's value MUST be an object.
308−-- Each value of this object MUST be either an object or an array.
309−--
310−-- If the value is an object, it MUST be a valid JSON Schema.
311−-- This is called a schema dependency.
312−--
313−-- If the value is an array, it MUST have at least one element.
314−-- Each element MUST be a string, and elements in the array MUST be unique.
315−-- This is called a property dependency.
316−dependencies :: ValidatorGen
317−dependencies spec g s (Object val) = do
318− let vs = hmToVector val
319− let schemaDeps = vs >>= toSchemaDep spec g
320− let propDeps = vs >>= toPropDep
321− when (V.length schemaDeps + V.length propDeps /= V.length vs) Nothing
322− Just (\x ->
323− case x of
324− Object y -> join $ (valSD <$> schemaDeps <*> pure y)
325− <> (valPD <$> propDeps <*> pure y)
326− _ -> mempty)
327− where
328− toSchemaDep :: Spec -> Graph -> (Text, Value) -> Vector (Text, Schema)
329− toSchemaDep spc gr (t, Object o) =
330− V.singleton (t, compile spc gr $ RawSchema (_rsURI s) o)
331− toSchemaDep _ _ _ = mempty
332−
333− toPropDep :: (Text, Value) -> Vector (Text, Vector Text)
334− toPropDep (t, Array a) =
335− if V.length a <= 0
336− then mempty
337− else case traverse toTxt a of
338− Nothing -> mempty
339− Just ts ->
340− if allUnique ts
341− then V.singleton (t, ts)
342− else mempty
343− toPropDep _ = mempty
344−
345− valSD :: (Text, Schema) -> HashMap Text Value -> Vector ValErr
346− valSD (t, sub) d =
347− case H.lookup t d of
348− Nothing -> mempty
349− Just _ -> validate sub (Object d)
350−
351− valPD :: (Text, Vector Text) -> HashMap Text Value -> Vector ValErr
352− valPD (t, ts) d =
353− case H.lookup t d of
354− Nothing -> mempty
355− Just _ ->
356− case traverse ($ d) (H.lookup <$> ts) of
357− Nothing -> V.singleton
358− ("Val error against property dependency with the key "
359− <> t <> " and the value " <> tshow ts <> " for: " <> tshow d)
360− Just _ -> mempty
361−
362−dependencies _ _ _ _ = Nothing
363−
364−--------------------------------------------------
365−-- * Any Validators
366−--------------------------------------------------
367−
368−enum :: ValidatorGen
369−enum _ _ _ (Array vs) = do
370− unless (V.length vs > 0 && allUnique vs) Nothing
371− Just (\x ->
372− if V.elem x vs
373− then mempty
374− else V.singleton $ tshow x <> " is not an element of enum " <> tshow vs)
375−enum _ _ _ _ = Nothing
376−
377−typeVal :: ValidatorGen
378−typeVal _ _ _ (String val) = Just (\x -> isJsonType x (V.singleton val))
379−typeVal _ _ _ (Array vs) = do
380− ts <- traverse toTxt vs
381− unless (allUnique ts) Nothing
382− Just (`isJsonType` ts)
383−typeVal _ _ _ _ = Nothing
384−
385−allOf :: ValidatorGen
386−allOf spec g s (Array vs) = do
387− os <- traverse toObj vs
388− let ss = compile spec g . RawSchema (_rsURI s) <$> os
389− Just (\x -> join $ validate <$> ss <*> pure x)
390−allOf _ _ _ _ = Nothing
391−
392−anyOf :: ValidatorGen
393−anyOf spec g s (Array vs) = do
394− os <- traverse toObj vs
395− let ss = compile spec g . RawSchema (_rsURI s) <$> os
396− Just (\x ->
397− if V.elem V.empty (validate <$> ss <*> pure x)
398− then mempty
399− else V.singleton ("Val error against anyOf " <> tshow vs <> " for: " <> tshow x))
400−anyOf _ _ _ _ = Nothing
401−
402−oneOf :: ValidatorGen
403−oneOf spec g s (Array vs) = do
404− os <- traverse toObj vs
405− let ss = compile spec g . RawSchema (_rsURI s) <$> os
406− Just (\x ->
407− if count V.empty (validate <$> ss <*> pure x) == 1
408− then mempty
409− else V.singleton ("Val error against oneOf " <> tshow vs <> " for: " <> tshow x))
410−oneOf _ _ _ _ = Nothing
411−
412−notValidator :: ValidatorGen
413−notValidator spec g s (Object val) = do
414− let sub = compile spec g (RawSchema (_rsURI s) val)
415− Just (\x ->
416− if V.null $ validate sub x
417− then V.singleton ("Val error against not validator " <> tshow val <> " for: " <> tshow x)
418− else mempty)
419−notValidator _ _ _ _ = Nothing
420−
421−-- http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03
422−--
423−-- TODO: Any members other than "$ref" in a JSON Reference object SHALL be
424−-- ignored.
425−ref :: ValidatorGen
426−ref spec g s (String val) = do
427− (reference, pointer) <- refAndPointer (_rsURI s `combineIdAndRef` val)
428− r <- RawSchema reference <$> H.lookup reference g
429− let urlDecoded = decodeUtf8 . urlDecode True . encodeUtf8 $ pointer
430− p <- eitherToMaybe $ jsonPointer urlDecoded
431− case resolvePointer p (Object $ _rsObject r) of
432− Right (Object o) ->
433− Just $ validate $ compile spec g $ RawSchema (_rsURI r) o
434− _ -> Nothing
435−ref _ _ _ _ = Nothing
436−
437−noVal :: ValidatorGen
438−noVal _ _ _ _ = Just (const V.empty)
src/Import.hsnew file+19
@@ -0,0 +1,19 @@
1+
2+module Import (module Export, fromJSONEither) where
3+
4+import Protolude as Export
5+
6+import Data.Aeson as Export
7+import Data.HashMap.Strict as Export (HashMap)
8+import Data.List.NonEmpty as Export (NonEmpty)
9+import Data.Vector as Export (Vector)
10+import Test.QuickCheck as Export hiding (Failure, Result, Success,
11+ (.&.))
12+
13+import qualified Data.Text as T
14+
15+fromJSONEither :: FromJSON a => Value -> Either Text a
16+fromJSONEither a =
17+ case fromJSON a of
18+ Error e -> Left (T.pack e)
19+ Success b -> Right b
src/JSONSchema/Draft4.hsnew file+186
@@ -0,0 +1,186 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+module JSONSchema.Draft4
4+ ( -- * Draft 4 Schema
5+ SchemaWithURI(..)
6+ , Schema(..)
7+ , SC.emptySchema
8+
9+ -- * One-step validation (getting references over HTTP)
10+ , fetchHTTPAndValidate
11+ , HTTPValidationFailure(..)
12+ , FE.HTTPFailure(..)
13+ , SchemaInvalid(..)
14+
15+ -- * One-step validation (getting references from the filesystem)
16+ , fetchFilesystemAndValidate
17+ , FilesystemValidationFailure(..)
18+ , FE.FilesystemFailure(..)
19+
20+ -- * Validation failure
21+ , Invalid(..)
22+ , ValidatorFailure(..)
23+
24+ -- * Fetching tools
25+ , URISchemaMap(..)
26+ , referencesViaHTTP
27+ , referencesViaFilesystem
28+
29+ -- * Other Draft 4 things exported just in case
30+ , metaSchema
31+ , metaSchemaBytes
32+ , schemaValidity
33+ , referencesValidity
34+ , checkSchema
35+ , draft4FetchInfo
36+ ) where
37+
38+import Import
39+
40+import qualified Data.ByteString as BS
41+import Data.FileEmbed (embedFile, makeRelativeToProject)
42+import qualified Data.HashMap.Strict as HM
43+import qualified Data.List.NonEmpty as NE
44+import Data.Maybe (fromMaybe)
45+
46+import JSONSchema.Draft4.Failure (Invalid(..), SchemaInvalid(..),
47+ ValidatorFailure(..))
48+import JSONSchema.Draft4.Schema (Schema)
49+import qualified JSONSchema.Draft4.Schema as SC
50+import qualified JSONSchema.Draft4.Spec as Spec
51+import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
52+import qualified JSONSchema.Fetch as FE
53+
54+data HTTPValidationFailure
55+ = HVRequest FE.HTTPFailure
56+ | HVSchema SchemaInvalid
57+ | HVData Invalid
58+ deriving Show
59+
60+-- | Fetch recursively referenced schemas over HTTP, check that both the
61+-- original and referenced schemas are valid, then validate then data.
62+fetchHTTPAndValidate
63+ :: SchemaWithURI Schema
64+ -> Value
65+ -> IO (Either HTTPValidationFailure ())
66+fetchHTTPAndValidate sw v = do
67+ res <- referencesViaHTTP sw
68+ pure (g =<< f =<< first HVRequest res)
69+ where
70+ f :: FE.URISchemaMap Schema
71+ -> Either HTTPValidationFailure (Value -> [ValidatorFailure])
72+ f references = first HVSchema (checkSchema references sw)
73+
74+ g :: (Value -> [ValidatorFailure]) -> Either HTTPValidationFailure ()
75+ g val = case NE.nonEmpty (val v) of
76+ Nothing -> Right ()
77+ Just failures -> Left (HVData Invalid
78+ { _invalidSchema = _swSchema sw
79+ , _invalidInstance = v
80+ , _invalidFailures = failures
81+ })
82+
83+data FilesystemValidationFailure
84+ = FVRead FE.FilesystemFailure
85+ | FVSchema SchemaInvalid
86+ | FVData Invalid
87+ deriving (Show, Eq)
88+
89+-- | Fetch recursively referenced schemas from the filesystem, check
90+-- that both the original and referenced schemas are valid, then validate
91+-- the data.
92+fetchFilesystemAndValidate
93+ :: SchemaWithURI Schema
94+ -> Value
95+ -> IO (Either FilesystemValidationFailure ())
96+fetchFilesystemAndValidate sw v = do
97+ res <- referencesViaFilesystem sw
98+ pure (g =<< f =<< first FVRead res)
99+ where
100+ f :: FE.URISchemaMap Schema
101+ -> Either FilesystemValidationFailure (Value -> [ValidatorFailure])
102+ f references = first FVSchema (checkSchema references sw)
103+
104+ g :: (Value -> [ValidatorFailure]) -> Either FilesystemValidationFailure ()
105+ g val = case NE.nonEmpty (val v) of
106+ Nothing -> Right ()
107+ Just invalid -> Left (FVData Invalid
108+ { _invalidSchema = _swSchema sw
109+ , _invalidInstance = v
110+ , _invalidFailures = invalid
111+ })
112+
113+-- | An instance of 'JSONSchema.Fetch.FetchInfo' specialized for
114+-- JSON Schema Draft 4.
115+draft4FetchInfo :: FE.FetchInfo Schema
116+draft4FetchInfo = FE.FetchInfo Spec.embedded SC._schemaId SC._schemaRef
117+
118+-- | Fetch the schemas recursively referenced by a starting schema over HTTP.
119+referencesViaHTTP
120+ :: SchemaWithURI Schema
121+ -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))
122+referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo
123+
124+-- | Fetch the schemas recursively referenced by a starting schema from
125+-- the filesystem.
126+referencesViaFilesystem
127+ :: SchemaWithURI Schema
128+ -> IO (Either FE.FilesystemFailure (FE.URISchemaMap Schema))
129+referencesViaFilesystem = FE.referencesViaFilesystem' draft4FetchInfo
130+
131+-- | Checks if a schema and a set of referenced schemas are valid.
132+--
133+-- Return a function to validate data.
134+checkSchema
135+ :: FE.URISchemaMap Schema
136+ -> SchemaWithURI Schema
137+ -> Either SchemaInvalid (Value -> [ValidatorFailure])
138+checkSchema sm sw =
139+ case NE.nonEmpty failures of
140+ Just fs -> Left (SchemaInvalid fs)
141+ Nothing -> Right (Spec.specValidate sm sw)
142+ where
143+ failures :: [(Maybe Text, NonEmpty ValidatorFailure)]
144+ failures =
145+ let refFailures = first Just <$> referencesValidity sm
146+ in case NE.nonEmpty (schemaValidity (_swSchema sw)) of
147+ Nothing -> refFailures
148+ Just errs -> (Nothing,errs) : refFailures
149+
150+metaSchema :: Schema
151+metaSchema =
152+ fromMaybe (panic "Schema decode failed (this should never happen)")
153+ . decodeStrict
154+ $ metaSchemaBytes
155+
156+metaSchemaBytes :: BS.ByteString
157+metaSchemaBytes =
158+ $(makeRelativeToProject "src/draft4.json" >>= embedFile)
159+
160+-- | Check that a schema itself is valid
161+-- (if so the returned list will be empty).
162+schemaValidity :: Schema -> [ValidatorFailure]
163+schemaValidity =
164+ Spec.specValidate schemaMap (SchemaWithURI metaSchema Nothing) . toJSON
165+ where
166+ schemaMap :: URISchemaMap Schema
167+ schemaMap =
168+ URISchemaMap (HM.singleton "http://json-schema.org/draft-04/schema"
169+ metaSchema)
170+
171+-- | Check that a set of referenced schemas are valid
172+-- (if so the returned list will be empty).
173+referencesValidity
174+ :: FE.URISchemaMap Schema
175+ -> [(Text, NonEmpty ValidatorFailure)]
176+ -- ^ The first item of the tuple is the URI of a schema, the second
177+ -- is that schema's validation errors.
178+referencesValidity = HM.foldlWithKey' f mempty . FE._unURISchemaMap
179+ where
180+ f :: [(Text, NonEmpty ValidatorFailure)]
181+ -> Text
182+ -> Schema
183+ -> [(Text, NonEmpty ValidatorFailure)]
184+ f acc k v = case NE.nonEmpty (schemaValidity v) of
185+ Nothing -> acc
186+ Just errs -> (k,errs) : acc
src/JSONSchema/Draft4/Failure.hsnew file+58
@@ -0,0 +1,58 @@
1+module JSONSchema.Draft4.Failure where
2+
3+import Import
4+
5+import JSONSchema.Draft4.Schema (Schema)
6+import qualified JSONSchema.Validator.Draft4 as VAL
7+
8+-- | Used to report an entire instance being invalidated, as opposed
9+-- to the failure of a single validator.
10+data Invalid = Invalid
11+ { _invalidSchema :: Schema
12+ , _invalidInstance :: Value
13+ , _invalidFailures :: NonEmpty ValidatorFailure
14+ } deriving (Eq, Show)
15+
16+data ValidatorFailure
17+ = FailureMultipleOf VAL.MultipleOfInvalid
18+ | FailureMaximum VAL.MaximumInvalid
19+ | FailureMinimum VAL.MinimumInvalid
20+
21+ | FailureMaxLength VAL.MaxLengthInvalid
22+ | FailureMinLength VAL.MinLengthInvalid
23+ | FailurePattern VAL.PatternInvalid
24+
25+ | FailureMaxItems VAL.MaxItemsInvalid
26+ | FailureMinItems VAL.MinItemsInvalid
27+ | FailureUniqueItems VAL.UniqueItemsInvalid
28+ | FailureItems (VAL.ItemsInvalid ValidatorFailure)
29+ | FailureAdditionalItems (VAL.AdditionalItemsInvalid ValidatorFailure)
30+
31+ | FailureMaxProperties VAL.MaxPropertiesInvalid
32+ | FailureMinProperties VAL.MinPropertiesInvalid
33+ | FailureRequired VAL.RequiredInvalid
34+ | FailureDependencies (VAL.DependenciesInvalid ValidatorFailure)
35+ | FailurePropertiesRelated (VAL.PropertiesRelatedInvalid ValidatorFailure)
36+
37+ | FailureRef (VAL.RefInvalid ValidatorFailure)
38+ | FailureEnum VAL.EnumInvalid
39+ | FailureType VAL.TypeValidatorInvalid
40+ | FailureAllOf (VAL.AllOfInvalid ValidatorFailure)
41+ | FailureAnyOf (VAL.AnyOfInvalid ValidatorFailure)
42+ | FailureOneOf (VAL.OneOfInvalid ValidatorFailure)
43+ | FailureNot VAL.NotValidatorInvalid
44+ deriving (Eq, Show)
45+
46+-- | A description of why a schema (or one of its reference) is itself
47+-- invalid.
48+--
49+-- 'Nothing' indicates the starting schema. 'Just' indicates a referenced
50+-- schema. The contents of the 'Just' is the schema's URI.
51+--
52+-- NOTE: 'HashMap (Maybe Text) Invalid' would be a nicer way of
53+-- defining this, but then we lose the guarantee that there's at least
54+-- one key.
55+newtype SchemaInvalid
56+ = SchemaInvalid {
57+ _unSchemaInvalid :: NonEmpty (Maybe Text, NonEmpty ValidatorFailure) }
58+ deriving (Eq, Show)
src/JSONSchema/Draft4/Schema.hsnew file+341
@@ -0,0 +1,341 @@
1+module JSONSchema.Draft4.Schema where
2+
3+import Import hiding (mapMaybe)
4+
5+import qualified Data.HashMap.Strict as HM
6+import Data.List.NonEmpty (NonEmpty)
7+import Data.Maybe (fromJust, isJust)
8+import Data.Scientific
9+import qualified Data.Set as Set
10+import qualified Data.Text as T
11+
12+import qualified JSONSchema.Validator.Draft4 as D4
13+import JSONSchema.Validator.Utils
14+
15+data Schema = Schema
16+ { _schemaVersion :: Maybe Text
17+ , _schemaId :: Maybe Text
18+ , _schemaRef :: Maybe Text
19+ , _schemaDefinitions :: Maybe (HashMap Text Schema)
20+ -- ^ A standardized location for embedding schemas
21+ -- to be referenced from elsewhere in the document.
22+ , _schemaOther :: HashMap Text Value
23+ -- ^ Since the JSON document this schema was built from could
24+ -- contain schemas anywhere (not just in "definitions" or any
25+ -- of the other official keys) we save any leftover key/value
26+ -- pairs not covered by them here.
27+ --
28+ -- TODO: This field is the source of most of the complication in this
29+ -- module and needs to be removed. It should be doable, though it will
30+ -- involve some modification to the fetching code.
31+
32+ , _schemaMultipleOf :: Maybe Scientific
33+ , _schemaMaximum :: Maybe Scientific
34+ , _schemaExclusiveMaximum :: Maybe Bool
35+ , _schemaMinimum :: Maybe Scientific
36+ , _schemaExclusiveMinimum :: Maybe Bool
37+
38+ , _schemaMaxLength :: Maybe Int
39+ , _schemaMinLength :: Maybe Int
40+ , _schemaPattern :: Maybe Text
41+
42+ , _schemaMaxItems :: Maybe Int
43+ , _schemaMinItems :: Maybe Int
44+ , _schemaUniqueItems :: Maybe Bool
45+ , _schemaItems :: Maybe (D4.Items Schema)
46+ -- Note that '_schemaAdditionalItems' is left out of 'runValidate'
47+ -- because its functionality is handled by '_schemaItems'. It always
48+ -- validates data unless 'Items' is present.
49+ , _schemaAdditionalItems :: Maybe (D4.AdditionalItems Schema)
50+
51+ , _schemaMaxProperties :: Maybe Int
52+ , _schemaMinProperties :: Maybe Int
53+ , _schemaRequired :: Maybe (Set Text)
54+ , _schemaDependencies :: Maybe (HashMap Text (D4.Dependency Schema))
55+ , _schemaProperties :: Maybe (HashMap Text Schema)
56+ , _schemaPatternProperties :: Maybe (HashMap Text Schema)
57+ , _schemaAdditionalProperties :: Maybe (D4.AdditionalProperties Schema)
58+
59+ , _schemaEnum :: Maybe (NonEmpty Value)
60+ , _schemaType :: Maybe D4.TypeValidator
61+ , _schemaAllOf :: Maybe (NonEmpty Schema)
62+ , _schemaAnyOf :: Maybe (NonEmpty Schema)
63+ , _schemaOneOf :: Maybe (NonEmpty Schema)
64+ , _schemaNot :: Maybe Schema
65+ } deriving (Eq, Show)
66+
67+emptySchema :: Schema
68+emptySchema = Schema
69+ { _schemaVersion = Nothing
70+ , _schemaId = Nothing
71+ , _schemaRef = Nothing
72+ , _schemaDefinitions = Nothing
73+ , _schemaOther = mempty
74+
75+ , _schemaMultipleOf = Nothing
76+ , _schemaMaximum = Nothing
77+ , _schemaExclusiveMaximum = Nothing
78+ , _schemaMinimum = Nothing
79+ , _schemaExclusiveMinimum = Nothing
80+
81+ , _schemaMaxLength = Nothing
82+ , _schemaMinLength = Nothing
83+ , _schemaPattern = Nothing
84+
85+ , _schemaMaxItems = Nothing
86+ , _schemaMinItems = Nothing
87+ , _schemaUniqueItems = Nothing
88+ , _schemaItems = Nothing
89+ , _schemaAdditionalItems = Nothing
90+
91+ , _schemaMaxProperties = Nothing
92+ , _schemaMinProperties = Nothing
93+ , _schemaRequired = Nothing
94+ , _schemaDependencies = Nothing
95+ , _schemaProperties = Nothing
96+ , _schemaPatternProperties = Nothing
97+ , _schemaAdditionalProperties = Nothing
98+
99+ , _schemaEnum = Nothing
100+ , _schemaType = Nothing
101+ , _schemaAllOf = Nothing
102+ , _schemaAnyOf = Nothing
103+ , _schemaOneOf = Nothing
104+ , _schemaNot = Nothing
105+ }
106+
107+instance FromJSON Schema where
108+ parseJSON = withObject "Schema" $ \o -> do
109+ a <- o .:! "$schema"
110+ b <- o .:! "id"
111+ c <- o .:! "$ref"
112+ d <- o .:! "definitions"
113+ e <- parseJSON (Object (HM.difference o internalSchemaHashMap))
114+
115+ f <- o .:! "multipleOf"
116+ g <- o .:! "maximum"
117+ h <- o .:! "exclusiveMaximum"
118+ i <- o .:! "minimum"
119+ j <- o .:! "exclusiveMinimum"
120+
121+ k <- o .:! "maxLength"
122+ l <- o .:! "minLength"
123+ m <- o .:! "pattern"
124+
125+ n <- o .:! "maxItems"
126+ o' <- o .:! "minItems"
127+ p <- o .:! "uniqueItems"
128+ q <- o .:! "items"
129+ r <- o .:! "additionalItems"
130+
131+ s <- o .:! "maxProperties"
132+ t <- o .:! "minProperties"
133+ u <- o .:! "required"
134+ v <- o .:! "dependencies"
135+ w <- o .:! "properties"
136+ x <- o .:! "patternProperties"
137+ y <- o .:! "additionalProperties"
138+
139+ z <- o .:! "enum"
140+ a2 <- o .:! "type"
141+ b2 <- fmap _unNonEmpty' <$> o .:! "allOf"
142+ c2 <- fmap _unNonEmpty' <$> o .:! "anyOf"
143+ d2 <- fmap _unNonEmpty' <$> o .:! "oneOf"
144+ e2 <- o .:! "not"
145+ pure Schema
146+ { _schemaVersion = a
147+ , _schemaId = b
148+ , _schemaRef = c
149+ , _schemaDefinitions = d
150+ , _schemaOther = e
151+
152+ , _schemaMultipleOf = f
153+ , _schemaMaximum = g
154+ , _schemaExclusiveMaximum = h
155+ , _schemaMinimum = i
156+ , _schemaExclusiveMinimum = j
157+
158+ , _schemaMaxLength = k
159+ , _schemaMinLength = l
160+ , _schemaPattern = m
161+
162+ , _schemaMaxItems = n
163+ , _schemaMinItems = o'
164+ , _schemaUniqueItems = p
165+ , _schemaItems = q
166+ , _schemaAdditionalItems = r
167+
168+ , _schemaMaxProperties = s
169+ , _schemaMinProperties = t
170+ , _schemaRequired = u
171+ , _schemaDependencies = v
172+ , _schemaProperties = w
173+ , _schemaPatternProperties = x
174+ , _schemaAdditionalProperties = y
175+
176+ , _schemaEnum = z
177+ , _schemaType = a2
178+ , _schemaAllOf = b2
179+ , _schemaAnyOf = c2
180+ , _schemaOneOf = d2
181+ , _schemaNot = e2
182+ }
183+
184+instance ToJSON Schema where
185+ -- | The way we resolve JSON Pointers to embedded schemas is by
186+ -- serializing the containing schema to a value and then resolving the
187+ -- pointer against it. This means that FromJSON and ToJSON must be
188+ -- isomorphic.
189+ --
190+ -- This influences the design choices in the library. E.g. right now
191+ -- there are two false values for "exclusiveMaximum" -- Nothing and
192+ -- Just False. We could have condensed them down by using () instead
193+ -- of Bool for "exclusiveMaximum". This would have made writing schemas
194+ -- in haskell easier, but we could no longer round trip through/from
195+ -- JSON without losing information.
196+ toJSON s = Object $ HM.union (mapMaybe ($ s) internalSchemaHashMap)
197+ (toJSON <$> _schemaOther s)
198+ where
199+ -- 'mapMaybe' is provided by unordered-containers after
200+ -- unordered-container-2.6.0.0, but until that is a little older
201+ -- (and has time to get into Stackage etc.) we use our own
202+ -- implementation.
203+ mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2
204+ mapMaybe f = fmap fromJust . HM.filter isJust . fmap f
205+
206+-- | Internal. Separate from ToJSON because it's also used
207+-- by FromJSON to determine what keys aren't official schema
208+-- keys and therefor should be included in _schemaOther.
209+internalSchemaHashMap :: HashMap Text (Schema -> Maybe Value)
210+internalSchemaHashMap = HM.fromList
211+ [ ("$schema" , f _schemaVersion)
212+ , ("id" , f _schemaId)
213+ , ("$ref" , f _schemaRef)
214+ , ("definitions" , f _schemaDefinitions)
215+
216+ , ("multipleOf" , f _schemaMultipleOf)
217+ , ("maximum" , f _schemaMaximum)
218+ , ("exclusiveMaximum" , f _schemaExclusiveMaximum)
219+ , ("minimum" , f _schemaMinimum)
220+ , ("exclusiveMinimum" , f _schemaExclusiveMinimum)
221+
222+ , ("maxLength" , f _schemaMaxLength)
223+ , ("minLength" , f _schemaMinLength)
224+ , ("pattern" , f _schemaPattern)
225+
226+ , ("maxItems" , f _schemaMaxItems)
227+ , ("minItems" , f _schemaMinItems)
228+ , ("uniqueItems" , f _schemaUniqueItems)
229+ , ("items" , f _schemaItems)
230+ , ("additionalItems" , f _schemaAdditionalItems)
231+
232+ , ("maxProperties" , f _schemaMaxProperties)
233+ , ("minProperties" , f _schemaMinProperties)
234+ , ("required" , f _schemaRequired)
235+ , ("dependencies" , f _schemaDependencies)
236+ , ("properties" , f _schemaProperties)
237+ , ("patternProperties" , f _schemaPatternProperties)
238+ , ("additionalProperties", f _schemaAdditionalProperties)
239+
240+ , ("enum" , f _schemaEnum)
241+ , ("type" , f _schemaType)
242+ , ("allOf" , f (fmap NonEmpty' . _schemaAllOf))
243+ , ("anyOf" , f (fmap NonEmpty' . _schemaAnyOf))
244+ , ("oneOf" , f (fmap NonEmpty' . _schemaOneOf))
245+ , ("not" , f _schemaNot)
246+ ]
247+ where
248+ f :: ToJSON a => (Schema -> Maybe a) -> Schema -> Maybe Value
249+ f = (fmap.fmap) toJSON
250+
251+instance Arbitrary Schema where
252+ arbitrary = sized f
253+ where
254+ maybeGen :: Gen a -> Gen (Maybe a)
255+ maybeGen a = oneof [pure Nothing, Just <$> a]
256+
257+ maybeRecurse :: Int -> Gen a -> Gen (Maybe a)
258+ maybeRecurse n a
259+ | n < 1 = pure Nothing
260+ | otherwise = maybeGen $ resize (n `div` 10) a
261+
262+ f :: Int -> Gen Schema
263+ f n = do
264+ a <- maybeGen arbitraryText
265+ b <- maybeGen arbitraryText
266+ c <- maybeGen arbitraryText
267+ -- NOTE: The next two fields are empty to generate cleaner
268+ -- schemas, but note that this means we don't test the
269+ -- invertability of these fields.
270+ d <- pure Nothing -- _schemaDefinitions
271+ e <- pure mempty -- _otherPairs
272+
273+ f' <- maybeGen arbitraryPositiveScientific
274+ g <- maybeGen arbitraryScientific
275+ h <- arbitrary
276+ i <- maybeGen arbitraryScientific
277+ j <- arbitrary
278+
279+ k <- maybeGen (getPositive <$> arbitrary)
280+ l <- maybeGen (getPositive <$> arbitrary)
281+ m <- maybeGen arbitraryText
282+
283+ n' <- maybeGen (getPositive <$> arbitrary)
284+ o <- maybeGen (getPositive <$> arbitrary)
285+ p <- arbitrary
286+ q <- maybeRecurse n arbitrary
287+ r <- maybeRecurse n arbitrary
288+
289+ s <- maybeGen (getPositive <$> arbitrary)
290+ t <- maybeGen (getPositive <$> arbitrary)
291+ u <- maybeGen (Set.map T.pack <$> arbitrary)
292+ v <- maybeRecurse n arbitraryHashMap
293+ w <- maybeRecurse n arbitraryHashMap
294+ x <- maybeRecurse n arbitraryHashMap
295+ y <- maybeRecurse n arbitrary
296+
297+ z <- maybeRecurse n ( fmap _unArbitraryValue . _unNonEmpty'
298+ <$> arbitrary)
299+ a2 <- arbitrary
300+ b2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
301+ c2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
302+ d2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)
303+ e2 <- maybeRecurse n arbitrary
304+ pure Schema
305+ { _schemaVersion = a
306+ , _schemaId = b
307+ , _schemaRef = c
308+ , _schemaDefinitions = d
309+ , _schemaOther = e
310+
311+ , _schemaMultipleOf = f'
312+ , _schemaMaximum = g
313+ , _schemaExclusiveMaximum = h
314+ , _schemaMinimum = i
315+ , _schemaExclusiveMinimum = j
316+
317+ , _schemaMaxLength = k
318+ , _schemaMinLength = l
319+ , _schemaPattern = m
320+
321+ , _schemaMaxItems = n'
322+ , _schemaMinItems = o
323+ , _schemaUniqueItems = p
324+ , _schemaItems = q
325+ , _schemaAdditionalItems = r
326+
327+ , _schemaMaxProperties = s
328+ , _schemaMinProperties = t
329+ , _schemaRequired = u
330+ , _schemaDependencies = v
331+ , _schemaProperties = w
332+ , _schemaPatternProperties = x
333+ , _schemaAdditionalProperties = y
334+
335+ , _schemaEnum = z
336+ , _schemaType = a2
337+ , _schemaAllOf = b2
338+ , _schemaAnyOf = c2
339+ , _schemaOneOf = d2
340+ , _schemaNot = e2
341+ }
src/JSONSchema/Draft4/Spec.hsnew file+150
@@ -0,0 +1,150 @@
1+module JSONSchema.Draft4.Spec where
2+
3+import Import
4+
5+import Data.Maybe (fromMaybe)
6+import Data.Profunctor (Profunctor(..))
7+
8+import JSONSchema.Draft4.Failure
9+import JSONSchema.Draft4.Schema (Schema(..), emptySchema)
10+import JSONSchema.Fetch (SchemaWithURI(..), URISchemaMap(..))
11+import qualified JSONSchema.Fetch as FE
12+import JSONSchema.Types (Spec(..))
13+import qualified JSONSchema.Types as JT
14+import JSONSchema.Validator.Draft4
15+import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
16+ updateResolutionScope)
17+
18+-- | An implementation of 'JT.embedded'.
19+embedded :: Schema -> ([Schema], [Schema])
20+embedded s =
21+ JT.embedded (d4Spec mempty mempty (Scope s Nothing (BaseURI Nothing))) s
22+
23+specValidate
24+ :: URISchemaMap Schema
25+ -> SchemaWithURI Schema
26+ -> Value
27+ -> [ValidatorFailure]
28+specValidate schemaMap sw =
29+ JT.validate (d4Spec schemaMap visited scope) (_swSchema sw)
30+ where
31+ visited :: VisitedSchemas
32+ visited = VisitedSchemas [(Nothing, Nothing)]
33+
34+ scope :: Scope Schema
35+ scope = Scope
36+ { _topLevelDocument = _swSchema sw
37+ , _documentURI = _swURI sw
38+ , _currentBaseURI = updateResolutionScope (BaseURI (_swURI sw))
39+ (_schemaId (_swSchema sw))
40+ }
41+
42+validateSubschema
43+ :: URISchemaMap Schema
44+ -> VisitedSchemas
45+ -> Scope Schema
46+ -> Schema
47+ -> Value
48+ -> [ValidatorFailure]
49+validateSubschema schemaMap visited scope schema =
50+ JT.validate (d4Spec schemaMap visited newScope) schema
51+ where
52+ newScope :: Scope Schema
53+ newScope = scope
54+ { _currentBaseURI = updateResolutionScope (_currentBaseURI scope)
55+ (_schemaId schema)
56+ }
57+
58+d4Spec
59+ :: URISchemaMap Schema
60+ -> VisitedSchemas
61+ -> Scope Schema
62+ -> Spec Schema ValidatorFailure
63+d4Spec schemaMap visited scope = Spec $
64+ [ dimap
65+ (fmap Ref . _schemaRef)
66+ FailureRef
67+ (refValidator (FE.getReference schemaMap) updateScope valRef visited scope)
68+ ]
69+
70+ <> fmap (lmap disableIfRefPresent)
71+ [ dimap (fmap MultipleOf . _schemaMultipleOf) FailureMultipleOf multipleOfValidator
72+ , dimap
73+ (\s -> Maximum (fromMaybe False (_schemaExclusiveMaximum s)) <$> _schemaMaximum s)
74+ FailureMaximum
75+ maximumValidator
76+ , dimap
77+ (\s -> Minimum (fromMaybe False (_schemaExclusiveMinimum s)) <$> _schemaMinimum s)
78+ FailureMinimum
79+ minimumValidator
80+
81+ , dimap (fmap MaxLength . _schemaMaxLength) FailureMaxLength maxLengthValidator
82+ , dimap (fmap MinLength . _schemaMinLength) FailureMinLength minLengthValidator
83+ , dimap (fmap PatternValidator . _schemaPattern) FailurePattern patternValidator
84+
85+ , dimap (fmap MaxItems . _schemaMaxItems) FailureMaxItems maxItemsValidator
86+ , dimap (fmap MinItems . _schemaMinItems) FailureMinItems minItemsValidator
87+ , dimap (fmap UniqueItems . _schemaUniqueItems) FailureUniqueItems uniqueItemsValidator
88+ , dimap
89+ (\s -> ItemsRelated
90+ { _irItems = _schemaItems s
91+ , _irAdditional = _schemaAdditionalItems s
92+ })
93+ (\err -> case err of
94+ IRInvalidItems e -> FailureItems e
95+ IRInvalidAdditional e -> FailureAdditionalItems e)
96+ (itemsRelatedValidator descend)
97+ , lmap (fmap Definitions . _schemaDefinitions) definitionsEmbedded
98+
99+ , dimap
100+ (fmap MaxProperties . _schemaMaxProperties)
101+ FailureMaxProperties
102+ maxPropertiesValidator
103+ , dimap
104+ (fmap MinProperties . _schemaMinProperties)
105+ FailureMinProperties
106+ minPropertiesValidator
107+ , dimap (fmap Required . _schemaRequired) FailureRequired requiredValidator
108+ , dimap
109+ (fmap DependenciesValidator . _schemaDependencies)
110+ FailureDependencies
111+ (dependenciesValidator descend)
112+ , dimap
113+ (\s -> PropertiesRelated
114+ { _propProperties = _schemaProperties s
115+ , _propPattern = _schemaPatternProperties s
116+ , _propAdditional = _schemaAdditionalProperties s
117+ })
118+ FailurePropertiesRelated
119+ (propertiesRelatedValidator descend)
120+
121+ , dimap (fmap EnumValidator . _schemaEnum) FailureEnum enumValidator
122+ , dimap (fmap TypeContext . _schemaType) FailureType typeValidator
123+ , dimap (fmap AllOf . _schemaAllOf) FailureAllOf (allOfValidator lateral)
124+ , dimap (fmap AnyOf . _schemaAnyOf) FailureAnyOf (anyOfValidator lateral)
125+ , dimap (fmap OneOf . _schemaOneOf) FailureOneOf (oneOfValidator lateral)
126+ , dimap (fmap NotValidator . _schemaNot) FailureNot (notValidator lateral)
127+ ]
128+ where
129+ disableIfRefPresent :: Schema -> Schema
130+ disableIfRefPresent schema =
131+ case _schemaRef schema of
132+ Nothing -> schema
133+ Just _ -> emptySchema
134+
135+ updateScope :: BaseURI -> Schema -> BaseURI
136+ updateScope uri schema = updateResolutionScope uri (_schemaId schema)
137+
138+ valRef
139+ :: VisitedSchemas
140+ -> Scope Schema
141+ -> Schema
142+ -> Value
143+ -> [ValidatorFailure]
144+ valRef vis sc = JT.validate (d4Spec schemaMap vis sc)
145+
146+ descend :: Schema -> Value -> [ValidatorFailure]
147+ descend = validateSubschema schemaMap mempty scope
148+
149+ lateral :: Schema -> Value -> [ValidatorFailure]
150+ lateral = validateSubschema schemaMap visited scope
src/JSONSchema/Fetch.hsnew file+175
@@ -0,0 +1,175 @@
1+module JSONSchema.Fetch where
2+
3+import Import
4+
5+import qualified Control.Exception.Safe as Safe
6+import Control.Monad (foldM)
7+import qualified Data.ByteString as BS
8+import qualified Data.ByteString.Lazy as LBS
9+import qualified Data.HashMap.Strict as HM
10+import qualified Data.Semigroup as S
11+import qualified Data.Text as T
12+import qualified Network.HTTP.Client as NC
13+import qualified Network.HTTP.Client.TLS as NCTLS
14+
15+import JSONSchema.Validator.Reference (BaseURI(..), resolveReference,
16+ updateResolutionScope)
17+
18+--------------------------------------------------
19+-- * Types
20+--------------------------------------------------
21+
22+-- | This is all the fetching functions need to know about a particular
23+-- JSON Schema draft, e.g. JSON Schema Draft 4.
24+data FetchInfo schema = FetchInfo
25+ { _fiEmbedded :: schema -> ([schema], [schema])
26+ , _fiId :: schema -> Maybe Text
27+ , _fiRef :: schema -> Maybe Text
28+ }
29+
30+-- | Keys are URIs (without URI fragments).
31+newtype URISchemaMap schema
32+ = URISchemaMap { _unURISchemaMap :: HashMap Text schema }
33+ deriving (Eq, Show, S.Semigroup, Monoid)
34+
35+-- | A top-level schema along with its location.
36+data SchemaWithURI schema = SchemaWithURI
37+ { _swSchema :: !schema
38+ , _swURI :: !(Maybe Text)
39+ -- ^ This is the URI from which the document was originally fetched.
40+ -- It's different than the schema's "id" field, which controls scope
41+ -- when resolving references contained in the schema.
42+ } deriving (Eq, Show)
43+
44+getReference :: URISchemaMap schema -> Text -> Maybe schema
45+getReference schemaMap t = HM.lookup t (_unURISchemaMap schemaMap)
46+
47+--------------------------------------------------
48+-- * Fetch via HTTP
49+--------------------------------------------------
50+
51+data HTTPFailure
52+ = HTTPParseFailure Text
53+ | HTTPRequestFailure NC.HttpException
54+ deriving Show
55+
56+-- | Take a schema. Retrieve every document either it or its subschemas
57+-- include via the "$ref" keyword.
58+referencesViaHTTP'
59+ :: forall schema. FromJSON schema
60+ => FetchInfo schema
61+ -> SchemaWithURI schema
62+ -> IO (Either HTTPFailure (URISchemaMap schema))
63+referencesViaHTTP' info sw = do
64+ manager <- NC.newManager NCTLS.tlsManagerSettings
65+ let f = referencesMethodAgnostic (getURL manager) info sw
66+ Safe.catch (first HTTPParseFailure <$> f) handler
67+ where
68+ getURL :: NC.Manager -> Text -> IO BS.ByteString
69+ getURL man url = do
70+ request <- NC.parseUrlThrow (T.unpack url)
71+ LBS.toStrict . NC.responseBody <$> NC.httpLbs request man
72+
73+ handler
74+ :: NC.HttpException
75+ -> IO (Either HTTPFailure (URISchemaMap schema))
76+ handler = pure . Left . HTTPRequestFailure
77+
78+--------------------------------------------------
79+-- * Fetch via Filesystem
80+--------------------------------------------------
81+
82+data FilesystemFailure
83+ = FSParseFailure Text
84+ | FSReadFailure IOException
85+ deriving (Show, Eq)
86+
87+referencesViaFilesystem'
88+ :: forall schema. FromJSON schema
89+ => FetchInfo schema
90+ -> SchemaWithURI schema
91+ -> IO (Either FilesystemFailure (URISchemaMap schema))
92+referencesViaFilesystem' info sw =
93+ Safe.catch (first FSParseFailure <$> f) handler
94+ where
95+ f :: IO (Either Text (URISchemaMap schema))
96+ f = referencesMethodAgnostic (BS.readFile . T.unpack) info sw
97+
98+ handler
99+ :: IOException
100+ -> IO (Either FilesystemFailure (URISchemaMap schema))
101+ handler = pure . Left . FSReadFailure
102+
103+--------------------------------------------------
104+-- * Method Agnostic Fetching Tools
105+--------------------------------------------------
106+
107+-- | A version of 'fetchReferencedSchema's where the function to fetch
108+-- schemas is provided by the user. This allows restrictions to be added,
109+-- e.g. rejecting non-local URIs.
110+referencesMethodAgnostic
111+ :: forall schema. FromJSON schema
112+ => (Text -> IO BS.ByteString)
113+ -> FetchInfo schema
114+ -> SchemaWithURI schema
115+ -> IO (Either Text (URISchemaMap schema))
116+referencesMethodAgnostic fetchRef info =
117+ getRecursiveReferences fetchRef info (URISchemaMap mempty)
118+
119+getRecursiveReferences
120+ :: forall schema. FromJSON schema
121+ => (Text -> IO BS.ByteString)
122+ -> FetchInfo schema
123+ -> URISchemaMap schema
124+ -> SchemaWithURI schema
125+ -> IO (Either Text (URISchemaMap schema))
126+getRecursiveReferences fetchRef info referenced sw =
127+ foldM f (Right referenced) (includeSubschemas info sw)
128+ where
129+ f :: Either Text (URISchemaMap schema)
130+ -> SchemaWithURI schema
131+ -> IO (Either Text (URISchemaMap schema))
132+ f (Left e) _ = pure (Left e)
133+ f (Right (URISchemaMap schemaMap)) (SchemaWithURI schema mURI) =
134+ case newRef of
135+ Nothing -> pure (Right (URISchemaMap schemaMap))
136+ Just uri -> do
137+ bts <- fetchRef uri
138+ case eitherDecodeStrict bts of
139+ Left e -> pure . Left . T.pack $ e
140+ Right s -> getRecursiveReferences
141+ fetchRef
142+ info
143+ (URISchemaMap (HM.insert uri s schemaMap))
144+ (SchemaWithURI s (Just uri))
145+ where
146+ newRef :: Maybe Text
147+ newRef = do
148+ ref <- _fiRef info schema
149+
150+ -- Consider the reference before updating the scope.
151+ -- If it's a only a fragment this isn't referencing
152+ -- a new document.
153+ void (fst (resolveReference (BaseURI Nothing) ref))
154+
155+ uri <- fst (resolveReference (BaseURI mURI) ref)
156+ case HM.lookup uri schemaMap of
157+ Nothing -> Just uri
158+ Just _ -> Nothing
159+
160+-- | Return the schema passed in as an argument, as well as every
161+-- subschema contained within it.
162+includeSubschemas
163+ :: forall schema.
164+ FetchInfo schema
165+ -> SchemaWithURI schema
166+ -> [SchemaWithURI schema]
167+includeSubschemas info (SchemaWithURI schema mURI) =
168+ SchemaWithURI schema mURI
169+ : (includeSubschemas info =<< subSchemas)
170+ where
171+ subSchemas :: [SchemaWithURI schema]
172+ subSchemas =
173+ let newScope = updateResolutionScope (BaseURI mURI) (_fiId info schema)
174+ updateScope s = SchemaWithURI s (_unBaseURI newScope)
175+ in updateScope <$> uncurry (<>) (_fiEmbedded info schema)
src/JSONSchema/Types.hsnew file+35
@@ -0,0 +1,35 @@
1+module JSONSchema.Types where
2+
3+import Import
4+
5+import JSONSchema.Validator.Types (Validator(..))
6+
7+newtype Spec schema err
8+ = Spec { _unSpec :: [Validator schema schema err] }
9+
10+-- | Return a schema's immediate subschemas.
11+--
12+-- The first list is subschemas validating the same level of the document,
13+-- the second list is subschemas validating lower levels (see
14+-- 'JSONSchema.Validator.Types.Fail' for a full explanation).
15+embedded :: Spec schema a -> schema -> ([schema], [schema])
16+embedded spec schema =
17+ let embeds = (\val -> _embedded val schema) <$> _unSpec spec
18+ in foldl' (\(a,b) (x,y) -> (x <> a, y <> b)) (mempty, mempty) embeds
19+
20+validate
21+ :: Spec schema err
22+ -> schema
23+ -> Value
24+ -> [err]
25+validate spec schema v =
26+ (\val -> _validate val schema v) =<< _unSpec spec
27+
28+-- | A basic schema type that doesn't impose much structure.
29+--
30+-- 'JSONSchema.Draft4' doesn't use this, but instead uses the record based
31+-- one defined in 'JSONSchema.Draft4.Schema' to make it easier to write
32+-- draft 4 schemas in Haskell.
33+newtype Schema
34+ = Schema { _unSchema :: HashMap Text Value }
35+ deriving (Eq, Show, FromJSON, ToJSON)
src/JSONSchema/Validator/Draft4.hsnew file+242
@@ -0,0 +1,242 @@
1+-- | Turn the validation functions into actual 'Validator's.
2+--
3+-- This is frankly a lot of busywork. It can perhaps be moved into the
4+-- validator modules themselves once we're sure this is the right design.
5+module JSONSchema.Validator.Draft4
6+ ( module JSONSchema.Validator.Draft4
7+ , module Export
8+ ) where
9+
10+import Import
11+
12+import qualified Data.HashMap.Strict as HM
13+import qualified Data.List.NonEmpty as NE
14+
15+import JSONSchema.Validator.Draft4.Any as Export
16+import JSONSchema.Validator.Draft4.Array as Export
17+import JSONSchema.Validator.Draft4.Number as Export
18+import JSONSchema.Validator.Draft4.Object as Export
19+import JSONSchema.Validator.Draft4.String as Export
20+import JSONSchema.Validator.Reference (BaseURI(..), Scope(..))
21+import JSONSchema.Validator.Types (Validator(..))
22+
23+-- | For internal use.
24+--
25+-- Take a validation function, a possibly existing validator, and some data.
26+-- If the validator is exists and can validate the type of data we have,
27+-- attempt to do so and return any failures.
28+run :: FromJSON b => (a -> b -> Maybe c) -> Maybe a -> Value -> [c]
29+run _ Nothing _ = mempty
30+run f (Just a) b =
31+ case fromJSONEither b of
32+ Left _ -> mempty
33+ Right c -> maybeToList (f a c)
34+
35+-- | For internal use.
36+noEmbedded :: a -> ([b], [b])
37+noEmbedded = const (mempty, mempty)
38+
39+--------------------------------------------------
40+-- * Numbers
41+--------------------------------------------------
42+
43+multipleOfValidator :: Validator a (Maybe MultipleOf) MultipleOfInvalid
44+multipleOfValidator = Validator noEmbedded (run multipleOfVal)
45+
46+maximumValidator :: Validator a (Maybe Maximum) MaximumInvalid
47+maximumValidator = Validator noEmbedded (run maximumVal)
48+
49+minimumValidator :: Validator a (Maybe Minimum) MinimumInvalid
50+minimumValidator = Validator noEmbedded (run minimumVal)
51+
52+--------------------------------------------------
53+-- * Strings
54+--------------------------------------------------
55+
56+maxLengthValidator :: Validator a (Maybe MaxLength) MaxLengthInvalid
57+maxLengthValidator = Validator noEmbedded (run maxLengthVal)
58+
59+minLengthValidator :: Validator a (Maybe MinLength) MinLengthInvalid
60+minLengthValidator = Validator noEmbedded (run minLengthVal)
61+
62+patternValidator :: Validator a (Maybe PatternValidator) PatternInvalid
63+patternValidator = Validator noEmbedded (run patternVal)
64+
65+--------------------------------------------------
66+-- * Arrays
67+--------------------------------------------------
68+
69+maxItemsValidator :: Validator a (Maybe MaxItems) MaxItemsInvalid
70+maxItemsValidator = Validator noEmbedded (run maxItemsVal)
71+
72+minItemsValidator :: Validator a (Maybe MinItems) MinItemsInvalid
73+minItemsValidator = Validator noEmbedded (run minItemsVal)
74+
75+uniqueItemsValidator :: Validator a (Maybe UniqueItems) UniqueItemsInvalid
76+uniqueItemsValidator = Validator noEmbedded (run uniqueItemsVal)
77+
78+-- TODO: Add tests to the language agnostic test suite to
79+-- make sure @"additionalItems"@ subschemas are embedded correctly.
80+itemsRelatedValidator
81+ :: (schema -> Value -> [err])
82+ -> Validator schema (ItemsRelated schema) (ItemsRelatedInvalid err)
83+itemsRelatedValidator f =
84+ Validator
85+ (\a -> ( mempty
86+ , case _irItems a of
87+ Just (ItemsObject b) -> pure b
88+ Just (ItemsArray cs) -> cs
89+ Nothing -> mempty
90+ <> case _irAdditional a of
91+ Just (AdditionalObject b) -> pure b
92+ _ -> mempty
93+ ))
94+ (\a b -> case fromJSONEither b of
95+ Left _ -> mempty
96+ Right c -> itemsRelatedVal f a c)
97+
98+--------------------------------------------------
99+-- * Objects
100+--------------------------------------------------
101+
102+maxPropertiesValidator
103+ :: Validator
104+ a
105+ (Maybe MaxProperties)
106+ MaxPropertiesInvalid
107+maxPropertiesValidator = Validator noEmbedded (run maxPropertiesVal)
108+
109+minPropertiesValidator
110+ :: Validator
111+ a
112+ (Maybe MinProperties)
113+ MinPropertiesInvalid
114+minPropertiesValidator = Validator noEmbedded (run minPropertiesVal)
115+
116+requiredValidator :: Validator a (Maybe Required) RequiredInvalid
117+requiredValidator = Validator noEmbedded (run requiredVal)
118+
119+dependenciesValidator
120+ :: (schema -> Value -> [err])
121+ -> Validator
122+ schema
123+ (Maybe (DependenciesValidator schema))
124+ (DependenciesInvalid err)
125+dependenciesValidator f =
126+ Validator
127+ (maybe mempty ( (\a -> (mempty, a))
128+ . catMaybes . fmap checkDependency
129+ . HM.elems . _unDependenciesValidator
130+ ))
131+ (run (dependenciesVal f))
132+ where
133+ checkDependency :: Dependency schema -> Maybe schema
134+ checkDependency (PropertyDependency _) = Nothing
135+ checkDependency (SchemaDependency s) = Just s
136+
137+propertiesRelatedValidator
138+ :: (schema -> Value -> [err])
139+ -> Validator
140+ schema
141+ (PropertiesRelated schema)
142+ (PropertiesRelatedInvalid err)
143+propertiesRelatedValidator f =
144+ Validator
145+ (\a -> ( mempty
146+ , HM.elems (fromMaybe mempty (_propProperties a))
147+ <> HM.elems (fromMaybe mempty (_propPattern a))
148+ <> case _propAdditional a of
149+ Just (AdditionalPropertiesObject b) -> [b]
150+ _ -> mempty
151+ ))
152+ (\a b -> case fromJSONEither b of
153+ Left _ -> mempty
154+ Right c -> maybeToList (propertiesRelatedVal f a c))
155+
156+newtype Definitions schema
157+ = Definitions { _unDefinitions :: HashMap Text schema }
158+ deriving (Eq, Show)
159+
160+instance FromJSON schema => FromJSON (Definitions schema) where
161+ parseJSON = withObject "Definitions" $ \o ->
162+ Definitions <$> o .: "definitions"
163+
164+definitionsEmbedded
165+ :: Validator
166+ schema
167+ (Maybe (Definitions schema))
168+ err
169+definitionsEmbedded =
170+ Validator
171+ (\a -> case a of
172+ Just (Definitions b) -> (mempty, HM.elems b)
173+ Nothing -> (mempty, mempty))
174+ (const (const mempty))
175+
176+--------------------------------------------------
177+-- * Any
178+--------------------------------------------------
179+
180+refValidator
181+ :: (FromJSON schema, ToJSON schema)
182+ => (Text -> Maybe schema)
183+ -> (BaseURI -> schema -> BaseURI)
184+ -> (VisitedSchemas -> Scope schema -> schema -> Value -> [err])
185+ -> VisitedSchemas
186+ -> Scope schema
187+ -> Validator a (Maybe Ref) (RefInvalid err)
188+refValidator getRef updateScope f visited scope =
189+ Validator
190+ noEmbedded
191+ (run (refVal getRef updateScope f visited scope))
192+
193+enumValidator :: Validator a (Maybe EnumValidator) EnumInvalid
194+enumValidator = Validator noEmbedded (run enumVal)
195+
196+typeValidator :: Validator a (Maybe TypeContext) TypeValidatorInvalid
197+typeValidator = Validator noEmbedded (run typeVal)
198+
199+allOfValidator
200+ :: (schema -> Value -> [err])
201+ -> Validator schema (Maybe (AllOf schema)) (AllOfInvalid err)
202+allOfValidator f =
203+ Validator
204+ (\a -> case a of
205+ Just (AllOf b) -> (NE.toList b, mempty)
206+ Nothing -> (mempty, mempty))
207+ (run (allOfVal f))
208+
209+anyOfValidator
210+ :: (schema -> Value -> [err])
211+ -> Validator schema (Maybe (AnyOf schema)) (AnyOfInvalid err)
212+anyOfValidator f =
213+ Validator
214+ (\a -> case a of
215+ Just (AnyOf b) -> (NE.toList b, mempty)
216+ Nothing -> (mempty, mempty))
217+ (run (anyOfVal f))
218+
219+oneOfValidator
220+ :: ToJSON schema
221+ => (schema -> Value -> [err])
222+ -> Validator schema (Maybe (OneOf schema)) (OneOfInvalid err)
223+oneOfValidator f =
224+ Validator
225+ (\a -> case a of
226+ Just (OneOf b) -> (NE.toList b, mempty)
227+ Nothing -> (mempty, mempty))
228+ (run (oneOfVal f))
229+
230+notValidator
231+ :: ToJSON schema
232+ => (schema -> Value -> [err])
233+ -> Validator
234+ schema
235+ (Maybe (NotValidator schema))
236+ NotValidatorInvalid
237+notValidator f =
238+ Validator
239+ (\a -> case a of
240+ Just (NotValidator b) -> (pure b, mempty)
241+ Nothing -> (mempty, mempty))
242+ (run (notVal f))
src/JSONSchema/Validator/Draft4/Any.hsnew file+509
@@ -0,0 +1,509 @@
1+module JSONSchema.Validator.Draft4.Any where
2+
3+import Import hiding ((<>))
4+
5+import Data.Aeson.TH (constructorTagModifier)
6+import Data.Char (toLower)
7+import qualified Data.HashMap.Strict as HM
8+import Data.List.NonEmpty (NonEmpty((:|)))
9+import qualified Data.List.NonEmpty as NE
10+import qualified Data.Scientific as SCI
11+import Data.Semigroup
12+import Data.Set (Set)
13+import qualified Data.Set as Set
14+import Data.Text.Encoding.Error (UnicodeException)
15+import qualified JSONPointer as JP
16+import Network.HTTP.Types.URI (urlDecode)
17+
18+import JSONSchema.Validator.Reference (BaseURI(..), Scope(..),
19+ URIAndFragment,
20+ resolveReference)
21+import qualified JSONSchema.Validator.Utils as UT
22+
23+--------------------------------------------------
24+-- * $ref
25+--------------------------------------------------
26+
27+newtype Ref
28+ = Ref { _unRef :: Text }
29+ deriving (Eq, Show)
30+
31+instance FromJSON Ref where
32+ parseJSON = withObject "Ref" $ \o ->
33+ Ref <$> o .: "$ref"
34+
35+data RefInvalid err
36+ = RefResolution Text
37+ -- ^ Indicates a reference that failed to resolve.
38+ --
39+ -- NOTE: The language agnostic test suite doesn't specify if this should
40+ -- cause a validation error or should allow data to pass. We choose to
41+ -- return a validation error.
42+ --
43+ -- Also note that ideally we would enforce in the type system that any
44+ -- failing references be dealt with before valididation. Then this could
45+ -- be removed entirely.
46+ | RefPointerResolution JSONPointerError
47+ | RefLoop Text VisitedSchemas URIAndFragment
48+ | RefInvalid Text Value (NonEmpty err)
49+ -- ^ 'Text' is the URI and 'Value' is the linked schema.
50+ deriving (Eq, Show)
51+
52+newtype VisitedSchemas
53+ = VisitedSchemas { _unVisited :: [URIAndFragment] }
54+ deriving (Eq, Show, Semigroup, Monoid)
55+
56+refVal
57+ :: forall err schema. (FromJSON schema, ToJSON schema)
58+ => (Text -> Maybe schema)
59+ -- ^ Look up a schema.
60+ -> (BaseURI -> schema -> BaseURI)
61+ -- ^ Update scope (needed after moving deeper into nested schemas).
62+ -> (VisitedSchemas -> Scope schema -> schema -> Value -> [err])
63+ -- ^ Validate data.
64+ -> VisitedSchemas
65+ -> Scope schema
66+ -> Ref
67+ -> Value
68+ -> Maybe (RefInvalid err)
69+refVal getRef updateScope val visited scope (Ref reference) x
70+ | (mURI, mFragment) `elem` _unVisited visited =
71+ Just (RefLoop reference visited (mURI, mFragment))
72+ | otherwise = leftToMaybe $ do
73+
74+ -- Get the referenced document
75+
76+ (newScope, doc) <- first RefResolution
77+ $ getDocument getRef updateScope scope mURI reference
78+
79+ -- Get the correct subschema within that document.
80+
81+ res <- case mFragment of
82+ Nothing -> Right (newScope, doc)
83+ Just fragment -> first RefPointerResolution
84+ $ resolveFragment updateScope newScope fragment
85+ let (finalScope, schema) = res
86+
87+ -- Check if that schema is valid.
88+
89+ let newVisited = VisitedSchemas [(_documentURI newScope, mFragment)]
90+ <> visited
91+ failures = val newVisited finalScope schema x
92+ first (RefInvalid reference (toJSON schema))
93+ . maybeToLeft ()
94+ $ NE.nonEmpty failures
95+ where
96+ mURI :: Maybe Text
97+ mFragment :: Maybe Text
98+ (mURI, mFragment) = resolveReference (_currentBaseURI scope) reference
99+
100+getDocument
101+ :: forall schema. (Text -> Maybe schema)
102+ -> (BaseURI -> schema -> BaseURI)
103+ -> Scope schema
104+ -> Maybe Text
105+ -> Text
106+ -> Either Text (Scope schema, schema)
107+ -- ^ 'Left' is the URI of the document we failed to resolve.
108+getDocument getRef updateScope scope mURI reference =
109+ case mURI <* fst (resolveReference (BaseURI Nothing) reference) of
110+ Nothing -> Right topOfThisDoc
111+ Just uri ->
112+ case getRef uri of
113+ Nothing -> Left uri
114+ Just s -> Right ( Scope s mURI (updateScope (BaseURI mURI) s)
115+ , s
116+ )
117+ where
118+ topOfThisDoc :: (Scope schema, schema)
119+ topOfThisDoc =
120+ ( scope { _currentBaseURI =
121+ updateScope (BaseURI (_documentURI scope))
122+ (_topLevelDocument scope)
123+ }
124+ , _topLevelDocument scope
125+ )
126+
127+data JSONPointerError
128+ = URLDecodingError UnicodeException
129+ -- ^ Aspirationally internal.
130+ | FormatError JP.FormatError
131+ | ResolutionError JP.ResolutionError
132+ | SubschemaDecodingError Text
133+ -- ^ Aspirationally internal.
134+ deriving (Eq, Show)
135+
136+resolveFragment
137+ :: (FromJSON schema, ToJSON schema)
138+ => (BaseURI -> schema -> BaseURI)
139+ -> Scope schema
140+ -> Text
141+ -> Either JSONPointerError (Scope schema, schema)
142+resolveFragment updateScope scope fragment = do
143+ urlDecoded <- first URLDecodingError
144+ . decodeUtf8'
145+ . urlDecode True
146+ . encodeUtf8
147+ $ fragment
148+ JP.Pointer tokens <- first FormatError (JP.unescape urlDecoded)
149+ let acc = (toJSON (_topLevelDocument scope), _currentBaseURI scope)
150+ (schemaVal, base) <- foldM go acc tokens
151+ schema <- first SubschemaDecodingError (fromJSONEither schemaVal)
152+ pure (scope { _currentBaseURI = base }, schema)
153+ where
154+ -- We have to step through the document JSON Pointer token
155+ -- by JSON Pointer token so that we can update the scope
156+ -- based on each @"id"@ we encounter.
157+ --
158+ -- TODO: Do we need specialized code to skip @"id"@s such
159+ -- as property keys that aren't meant to change scope?
160+ -- Perhaps this should be added to the language agnostic
161+ -- test suite as well.
162+ --
163+ -- In the meantime 'newBaseURIFromFragment' drops all keys
164+ -- from JSON objects except "id", which at least prevents
165+ -- SubschemaDecodingError in a situation where one of the
166+ -- values we step through isn't a valid schema.
167+ go :: (Value, BaseURI)
168+ -> JP.Token
169+ -> Either JSONPointerError (Value, BaseURI)
170+ go (lastVal, baseURI) tok = do
171+ v <- first ResolutionError (JP.resolveToken tok lastVal)
172+ newBase <- newBaseURIFromFragment updateScope baseURI v
173+ Right (v, newBase)
174+
175+-- | Update the 'BaseURI' (the store of the current "id" value)
176+-- after resolving one token of a JSON Pointer and stepping into
177+-- a new 'Value'.
178+newBaseURIFromFragment
179+ :: FromJSON schema
180+ => (BaseURI -> schema -> BaseURI)
181+ -> BaseURI
182+ -> Value
183+ -> Either JSONPointerError BaseURI
184+newBaseURIFromFragment updateScope baseURI v =
185+ case v of
186+ Object hm -> do
187+ let hmWithOnlyId = case HM.lookup idKey hm of
188+ Nothing -> mempty
189+ Just idVal -> HM.singleton idKey idVal
190+ schema <- first SubschemaDecodingError (fromJSONEither (Object hmWithOnlyId))
191+ Right (updateScope baseURI schema)
192+ _ -> Right baseURI
193+ where
194+ idKey :: Text
195+ idKey = "id"
196+
197+--------------------------------------------------
198+-- * enum
199+--------------------------------------------------
200+
201+-- | From the spec:
202+-- <http://json-schema.org/latest/json-schema-validation.html#anchor76>
203+--
204+-- > The value of this keyword MUST be an array.
205+-- > This array MUST have at least one element.
206+-- > Elements in the array MUST be unique.
207+--
208+-- NOTE: We don't enforce the uniqueness constraint in the haskell code,
209+-- but we do in the 'FromJSON' instance.
210+newtype EnumValidator
211+ = EnumValidator { _unEnumValidator :: NonEmpty Value }
212+ -- Given a choice, we'd prefer to enforce uniqueness through the type
213+ -- system over having at least one element. To use a 'Set' though we'd
214+ -- have to use 'OrdValue' here (there's no 'Ord' instance for plain Values)
215+ -- and we'd rather not make users mess with 'OrdValue'.
216+ deriving (Eq, Show)
217+
218+instance FromJSON EnumValidator where
219+ parseJSON = withObject "EnumValidator" $ \o ->
220+ EnumValidator <$> o .: "enum"
221+
222+instance Arbitrary EnumValidator where
223+ arbitrary = do
224+ xs <- (fmap.fmap) UT._unArbitraryValue arbitrary
225+ case NE.nonEmpty (toUnique xs) of
226+ Nothing -> EnumValidator . pure . UT._unArbitraryValue <$> arbitrary
227+ Just ne -> pure (EnumValidator ne)
228+ where
229+ toUnique :: [Value] -> [Value]
230+ toUnique = fmap UT._unOrdValue
231+ . Set.toList
232+ . Set.fromList
233+ . fmap UT.OrdValue
234+
235+data EnumInvalid
236+ = EnumInvalid EnumValidator Value
237+ deriving (Eq, Show)
238+
239+enumVal :: EnumValidator -> Value -> Maybe EnumInvalid
240+enumVal a@(EnumValidator vs) x
241+ | not (UT.allUniqueValues vs) = Nothing
242+ | x `elem` vs = Nothing
243+ | otherwise = Just $ EnumInvalid a x
244+
245+--------------------------------------------------
246+-- * type
247+--------------------------------------------------
248+
249+-- | This is separate from 'TypeValidator' so that 'TypeValidator' can
250+-- be used to write 'JSONSchema.Draft4.Schema.Schema' without
251+-- messing up the 'FromJSON' instance of that data type.
252+newtype TypeContext
253+ = TypeContext { _unTypeContext :: TypeValidator }
254+ deriving (Eq, Show)
255+
256+instance FromJSON TypeContext where
257+ parseJSON = withObject "TypeContext" $ \o ->
258+ TypeContext <$> o .: "type"
259+
260+data TypeValidator
261+ = TypeValidatorString SchemaType
262+ | TypeValidatorArray (Set SchemaType)
263+ deriving (Eq, Show)
264+
265+instance Semigroup TypeValidator where
266+ (<>) x y
267+ | isEmpty x = x
268+ | isEmpty y = y
269+ | x == y = x
270+ | otherwise = TypeValidatorArray (setFromTypeValidator x
271+ `Set.union`
272+ setFromTypeValidator y)
273+ where
274+ isEmpty :: TypeValidator -> Bool
275+ isEmpty (TypeValidatorString _) = False
276+ isEmpty (TypeValidatorArray ts) = Set.null ts
277+
278+ stimes = stimesIdempotent
279+
280+instance FromJSON TypeValidator where
281+ parseJSON v = fmap TypeValidatorString (parseJSON v)
282+ <|> fmap TypeValidatorArray (parseJSON v)
283+
284+instance ToJSON TypeValidator where
285+ toJSON (TypeValidatorString t) = toJSON t
286+ toJSON (TypeValidatorArray ts) = toJSON ts
287+
288+instance Arbitrary TypeValidator where
289+ arbitrary = oneof [ TypeValidatorString <$> arbitrary
290+ , TypeValidatorArray <$> arbitrary
291+ ]
292+
293+data SchemaType
294+ = SchemaObject
295+ | SchemaArray
296+ | SchemaString
297+ | SchemaNumber
298+ | SchemaInteger
299+ | SchemaBoolean
300+ | SchemaNull
301+ deriving (Eq, Ord, Show, Bounded, Enum, Generic)
302+
303+instance FromJSON SchemaType where
304+ parseJSON = genericParseJSON
305+ defaultOptions
306+ { constructorTagModifier = fmap toLower . drop 6 }
307+
308+instance ToJSON SchemaType where
309+ toJSON = genericToJSON
310+ defaultOptions
311+ { constructorTagModifier = fmap toLower . drop 6 }
312+
313+instance Arbitrary SchemaType where
314+ arbitrary = arbitraryBoundedEnum
315+
316+data TypeValidatorInvalid
317+ = TypeValidatorInvalid TypeValidator Value
318+ deriving (Eq, Show)
319+
320+typeVal :: TypeContext -> Value -> Maybe TypeValidatorInvalid
321+typeVal (TypeContext tv) x
322+ | Set.null matches = Just (TypeValidatorInvalid tv x)
323+ | otherwise = Nothing
324+ where
325+ -- There can be more than one match because a 'Value' can be both a
326+ -- @"number"@ and an @"integer"@.
327+ matches :: Set SchemaType
328+ matches = Set.intersection okTypes (setFromTypeValidator tv)
329+
330+ okTypes :: Set SchemaType
331+ okTypes =
332+ case x of
333+ Null -> Set.singleton SchemaNull
334+ (Array _) -> Set.singleton SchemaArray
335+ (Bool _) -> Set.singleton SchemaBoolean
336+ (Object _) -> Set.singleton SchemaObject
337+ (String _) -> Set.singleton SchemaString
338+ (Number y) ->
339+ if SCI.isInteger y
340+ then Set.fromList [SchemaNumber, SchemaInteger]
341+ else Set.singleton SchemaNumber
342+
343+-- | Internal.
344+setFromTypeValidator :: TypeValidator -> Set SchemaType
345+setFromTypeValidator (TypeValidatorString t) = Set.singleton t
346+setFromTypeValidator (TypeValidatorArray ts) = ts
347+
348+--------------------------------------------------
349+-- * allOf
350+--------------------------------------------------
351+
352+newtype AllOf schema
353+ = AllOf { _unAllOf :: NonEmpty schema }
354+ deriving (Eq, Show)
355+
356+instance FromJSON schema => FromJSON (AllOf schema) where
357+ parseJSON = withObject "AllOf" $ \o ->
358+ AllOf <$> o .: "allOf"
359+
360+newtype AllOfInvalid err
361+ = AllOfInvalid (NonEmpty (JP.Index, NonEmpty err))
362+ deriving (Eq, Show)
363+
364+allOfVal
365+ :: forall err schema.
366+ (schema -> Value -> [err])
367+ -> AllOf schema
368+ -> Value
369+ -> Maybe (AllOfInvalid err)
370+allOfVal f (AllOf subSchemas) x = AllOfInvalid <$> NE.nonEmpty failures
371+ where
372+ perhapsFailures :: [(JP.Index, [err])]
373+ perhapsFailures = zip (JP.Index <$> [0..])
374+ (flip f x <$> NE.toList subSchemas)
375+
376+ failures :: [(JP.Index, NonEmpty err)]
377+ failures = mapMaybe (traverse NE.nonEmpty) perhapsFailures
378+
379+--------------------------------------------------
380+-- * anyOf
381+--------------------------------------------------
382+
383+newtype AnyOf schema
384+ = AnyOf { _unAnyOf :: NonEmpty schema }
385+ deriving (Eq, Show)
386+
387+instance FromJSON schema => FromJSON (AnyOf schema) where
388+ parseJSON = withObject "AnyOf" $ \o ->
389+ AnyOf <$> o .: "anyOf"
390+
391+newtype AnyOfInvalid err
392+ = AnyOfInvalid (NonEmpty (JP.Index, NonEmpty err))
393+ deriving (Eq, Show)
394+
395+anyOfVal
396+ :: forall err schema.
397+ (schema -> Value -> [err])
398+ -> AnyOf schema
399+ -> Value
400+ -> Maybe (AnyOfInvalid err)
401+anyOfVal f (AnyOf subSchemas) x
402+ | any (null . snd) perhapsFailures = Nothing
403+ | otherwise = AnyOfInvalid <$> NE.nonEmpty failures
404+ where
405+ perhapsFailures :: [(JP.Index, [err])]
406+ perhapsFailures = zip (JP.Index <$> [0..])
407+ (flip f x <$> NE.toList subSchemas)
408+
409+ failures :: [(JP.Index, NonEmpty err)]
410+ failures = mapMaybe (traverse NE.nonEmpty) perhapsFailures
411+
412+--------------------------------------------------
413+-- * oneOf
414+--------------------------------------------------
415+
416+newtype OneOf schema
417+ = OneOf { _unOneOf :: NonEmpty schema }
418+ deriving (Eq, Show)
419+
420+instance FromJSON schema => FromJSON (OneOf schema) where
421+ parseJSON = withObject "OneOf" $ \o ->
422+ OneOf <$> o .: "oneOf"
423+
424+data OneOfInvalid err
425+ = TooManySuccesses (NonEmpty (JP.Index, Value)) Value
426+ -- ^ The NonEmpty lists contains tuples whose contents
427+ -- are the index of a schema that validated the data
428+ -- and the contents of that schema.
429+ | NoSuccesses (NonEmpty (JP.Index, NonEmpty err)) Value
430+ -- ^ The NonEmpty lists contains tuples whose contents
431+ -- are the index of a schema that failed to validate the data
432+ -- and the failures it produced.
433+ deriving (Eq, Show)
434+
435+oneOfVal
436+ :: forall err schema. ToJSON schema
437+ => (schema -> Value -> [err])
438+ -> OneOf schema
439+ -> Value
440+ -> Maybe (OneOfInvalid err)
441+oneOfVal f (OneOf (firstSubSchema :| otherSubSchemas)) x =
442+ -- Producing the NonEmpty lists needed by the error constructors
443+ -- is a little tricky. If we had a partition function like this
444+ -- it might help:
445+ -- @
446+ -- (a -> Either b c) -> NonEmpty a -> Either (NonEmpty b, [c])
447+ -- ([b], NonEmpty c)
448+ -- @
449+ case (firstSuccess, otherSuccesses) of
450+ (Right _, Nothing) -> Nothing
451+ (Right a, Just successes) -> Just (TooManySuccesses
452+ (a NE.<| successes) x)
453+ (Left e, Nothing) -> Just (NoSuccesses (e :| otherFailures) x)
454+ (Left _, Just (_ :| [])) -> Nothing
455+ (Left _, Just successes) -> Just (TooManySuccesses successes x)
456+ where
457+ firstSuccess :: Either (JP.Index, NonEmpty err) (JP.Index, Value)
458+ firstSuccess =
459+ case NE.nonEmpty (f firstSubSchema x) of
460+ Nothing -> Right (JP.Index 0, toJSON firstSubSchema)
461+ Just errs -> Left (JP.Index 0, errs)
462+
463+ otherPerhapsFailures :: [(JP.Index, Value, [err])]
464+ otherPerhapsFailures =
465+ zipWith
466+ (\index schema -> (index, toJSON schema, f schema x))
467+ (JP.Index <$> [0..])
468+ otherSubSchemas
469+
470+ otherSuccesses :: Maybe (NonEmpty (JP.Index, Value))
471+ otherSuccesses = NE.nonEmpty
472+ $ mapMaybe (\(index,val,errs) ->
473+ case errs of
474+ [] -> Just (index,val)
475+ _ -> Nothing
476+ ) otherPerhapsFailures
477+
478+ otherFailures :: [(JP.Index, NonEmpty err)]
479+ otherFailures = mapMaybe (traverse NE.nonEmpty . mid) otherPerhapsFailures
480+
481+ mid :: (a,b,c) -> (a,c)
482+ mid (a,_,c) = (a,c)
483+
484+--------------------------------------------------
485+-- * not
486+--------------------------------------------------
487+
488+newtype NotValidator schema
489+ = NotValidator { _unNotValidator :: schema }
490+ deriving (Eq, Show)
491+
492+instance FromJSON schema => FromJSON (NotValidator schema) where
493+ parseJSON = withObject "NotValidator" $ \o ->
494+ NotValidator <$> o .: "not"
495+
496+data NotValidatorInvalid
497+ = NotValidatorInvalid Value Value
498+ deriving (Eq, Show)
499+
500+notVal
501+ :: ToJSON schema =>
502+ (schema -> Value -> [err])
503+ -> NotValidator schema
504+ -> Value
505+ -> Maybe NotValidatorInvalid
506+notVal f (NotValidator schema) x =
507+ case f schema x of
508+ [] -> Just (NotValidatorInvalid (toJSON schema) x)
509+ _ -> Nothing
src/JSONSchema/Validator/Draft4/Array.hsnew file+225
@@ -0,0 +1,225 @@
1+module JSONSchema.Validator.Draft4.Array where
2+
3+import Import
4+
5+import qualified Data.List.NonEmpty as NE
6+import qualified Data.Vector as V
7+import qualified JSONPointer as JP
8+
9+import JSONSchema.Validator.Utils (allUniqueValues)
10+
11+--------------------------------------------------
12+-- * maxItems
13+--------------------------------------------------
14+
15+newtype MaxItems
16+ = MaxItems { _unMaxItems :: Int }
17+ deriving (Eq, Show)
18+
19+instance FromJSON MaxItems where
20+ parseJSON = withObject "MaxItems" $ \o ->
21+ MaxItems <$> o .: "maxItems"
22+
23+data MaxItemsInvalid
24+ = MaxItemsInvalid MaxItems (Vector Value)
25+ deriving (Eq, Show)
26+
27+-- | The spec requires @"maxItems"@ to be non-negative.
28+maxItemsVal :: MaxItems -> Vector Value -> Maybe MaxItemsInvalid
29+maxItemsVal a@(MaxItems n) xs
30+ | n < 0 = Nothing
31+ | V.length xs > n = Just (MaxItemsInvalid a xs)
32+ | otherwise = Nothing
33+
34+--------------------------------------------------
35+-- * minItems
36+--------------------------------------------------
37+
38+newtype MinItems
39+ = MinItems { _unMinItems :: Int }
40+ deriving (Eq, Show)
41+
42+instance FromJSON MinItems where
43+ parseJSON = withObject "MinItems" $ \o ->
44+ MinItems <$> o .: "minItems"
45+
46+data MinItemsInvalid
47+ = MinItemsInvalid MinItems (Vector Value)
48+ deriving (Eq, Show)
49+
50+-- | The spec requires @"minItems"@ to be non-negative.
51+minItemsVal :: MinItems -> Vector Value -> Maybe MinItemsInvalid
52+minItemsVal a@(MinItems n) xs
53+ | n < 0 = Nothing
54+ | V.length xs < n = Just (MinItemsInvalid a xs)
55+ | otherwise = Nothing
56+
57+--------------------------------------------------
58+-- * uniqueItems
59+--------------------------------------------------
60+
61+newtype UniqueItems
62+ = UniqueItems { _unUniqueItems :: Bool }
63+ deriving (Eq, Show)
64+
65+instance FromJSON UniqueItems where
66+ parseJSON = withObject "UniqueItems" $ \o ->
67+ UniqueItems <$> o .: "uniqueItems"
68+
69+newtype UniqueItemsInvalid
70+ = UniqueItemsInvalid (Vector Value)
71+ deriving (Eq, Show)
72+
73+uniqueItemsVal :: UniqueItems -> Vector Value -> Maybe UniqueItemsInvalid
74+uniqueItemsVal (UniqueItems True) xs
75+ | allUniqueValues xs = Nothing
76+ | otherwise = Just (UniqueItemsInvalid xs)
77+uniqueItemsVal (UniqueItems False) _ = Nothing
78+
79+--------------------------------------------------
80+-- * items
81+--------------------------------------------------
82+
83+data ItemsRelated schema = ItemsRelated
84+ { _irItems :: Maybe (Items schema)
85+ , _irAdditional :: Maybe (AdditionalItems schema)
86+ } deriving (Eq, Show)
87+
88+instance FromJSON schema => FromJSON (ItemsRelated schema) where
89+ parseJSON = withObject "ItemsRelated" $ \o -> ItemsRelated
90+ <$> o .:! "items"
91+ <*> o .:! "additionalItems"
92+
93+emptyItems :: ItemsRelated schema
94+emptyItems = ItemsRelated
95+ { _irItems = Nothing
96+ , _irAdditional = Nothing
97+ }
98+
99+data Items schema
100+ = ItemsObject schema
101+ | ItemsArray [schema]
102+ deriving (Eq, Show)
103+
104+instance FromJSON schema => FromJSON (Items schema) where
105+ parseJSON v = fmap ItemsObject (parseJSON v)
106+ <|> fmap ItemsArray (parseJSON v)
107+
108+instance ToJSON schema => ToJSON (Items schema) where
109+ toJSON (ItemsObject hm) = toJSON hm
110+ toJSON (ItemsArray schemas) = toJSON schemas
111+
112+instance Arbitrary schema => Arbitrary (Items schema) where
113+ arbitrary = oneof [ ItemsObject <$> arbitrary
114+ , ItemsArray <$> arbitrary
115+ ]
116+
117+data ItemsRelatedInvalid err
118+ = IRInvalidItems (ItemsInvalid err)
119+ | IRInvalidAdditional (AdditionalItemsInvalid err)
120+ deriving (Eq, Show)
121+
122+data ItemsInvalid err
123+ = ItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
124+ | ItemsArrayInvalid (NonEmpty (JP.Index, NonEmpty err))
125+ deriving (Eq, Show)
126+
127+-- | @"additionalItems"@ only matters if @"items"@ exists
128+-- and is a JSON Array.
129+itemsRelatedVal
130+ :: forall err schema.
131+ (schema -> Value -> [err])
132+ -> ItemsRelated schema
133+ -> Vector Value
134+ -> [ItemsRelatedInvalid err] -- NOTE: 'Data.These' would help here.
135+itemsRelatedVal f a xs =
136+ let (itemsFailure, remaining) = case _irItems a of
137+ Nothing -> (Nothing, mempty)
138+ Just b -> itemsVal f b xs
139+ additionalFailure = (\b -> additionalItemsVal f b remaining)
140+ =<< _irAdditional a
141+ in catMaybes [ IRInvalidItems <$> itemsFailure
142+ , IRInvalidAdditional <$> additionalFailure
143+ ]
144+
145+-- | Internal.
146+--
147+-- This is because 'itemsRelated' handles @"items"@ validation.
148+itemsVal
149+ :: forall err schema.
150+ (schema -> Value -> [err])
151+ -> Items schema
152+ -> Vector Value
153+ -> (Maybe (ItemsInvalid err), [(JP.Index, Value)])
154+ -- ^ The second item in the tuple is the elements of the original
155+ -- JSON Array still remaining to be checked by @"additionalItems"@.
156+itemsVal f a xs =
157+ case a of
158+ ItemsObject subSchema ->
159+ case NE.nonEmpty (mapMaybe (validateElem subSchema) indexed) of
160+ Nothing -> (Nothing, mempty)
161+ Just errs -> (Just (ItemsObjectInvalid errs), mempty)
162+ ItemsArray subSchemas ->
163+ let remaining = drop (length subSchemas) indexed
164+ res = catMaybes (zipWith validateElem subSchemas indexed)
165+ in case NE.nonEmpty res of
166+ Nothing -> (Nothing, remaining)
167+ Just errs -> (Just (ItemsArrayInvalid errs), remaining)
168+ where
169+ indexed :: [(JP.Index, Value)]
170+ indexed = zip (JP.Index <$> [0..]) (V.toList xs)
171+
172+ validateElem
173+ :: schema
174+ -> (JP.Index, Value)
175+ -> Maybe (JP.Index, NonEmpty err)
176+ validateElem schema (index,x) =
177+ (\v -> (index, v)) <$> NE.nonEmpty (f schema x)
178+
179+--------------------------------------------------
180+-- * additionalItems
181+--------------------------------------------------
182+
183+data AdditionalItems schema
184+ = AdditionalBool Bool
185+ | AdditionalObject schema
186+ deriving (Eq, Show)
187+
188+instance FromJSON schema => FromJSON (AdditionalItems schema) where
189+ parseJSON v = fmap AdditionalBool (parseJSON v)
190+ <|> fmap AdditionalObject (parseJSON v)
191+
192+instance ToJSON schema => ToJSON (AdditionalItems schema) where
193+ toJSON (AdditionalBool b) = toJSON b
194+ toJSON (AdditionalObject hm) = toJSON hm
195+
196+instance Arbitrary schema => Arbitrary (AdditionalItems schema) where
197+ arbitrary = oneof [ AdditionalBool <$> arbitrary
198+ , AdditionalObject <$> arbitrary
199+ ]
200+
201+data AdditionalItemsInvalid err
202+ = AdditionalItemsBoolInvalid (NonEmpty (JP.Index, Value))
203+ | AdditionalItemsObjectInvalid (NonEmpty (JP.Index, NonEmpty err))
204+ deriving (Eq, Show)
205+
206+-- | Internal.
207+--
208+-- This is because 'itemsRelated' handles @"additionalItems"@ validation.
209+additionalItemsVal
210+ :: forall err schema.
211+ (schema -> Value -> [err])
212+ -> AdditionalItems schema
213+ -> [(JP.Index, Value)]
214+ -- ^ The elements remaining to validate after the ones covered by
215+ -- @"items"@ have been removed.
216+ -> Maybe (AdditionalItemsInvalid err)
217+additionalItemsVal _ (AdditionalBool True) _ = Nothing
218+additionalItemsVal _ (AdditionalBool False) xs =
219+ AdditionalItemsBoolInvalid <$> NE.nonEmpty xs
220+additionalItemsVal f (AdditionalObject subSchema) xs =
221+ let res = mapMaybe
222+ (\(index,x) -> (\v -> (index, v))
223+ <$> NE.nonEmpty (f subSchema x))
224+ xs
225+ in AdditionalItemsObjectInvalid <$> NE.nonEmpty res
src/JSONSchema/Validator/Draft4/Number.hsnew file+83
@@ -0,0 +1,83 @@
1+module JSONSchema.Validator.Draft4.Number where
2+
3+import Import
4+
5+import Data.Fixed (mod')
6+import Data.Scientific (Scientific)
7+
8+--------------------------------------------------
9+-- * multipleOf
10+--------------------------------------------------
11+
12+newtype MultipleOf
13+ = MultipleOf { _unMultipleOf :: Scientific }
14+ deriving (Eq, Show)
15+
16+instance FromJSON MultipleOf where
17+ parseJSON = withObject "MultipleOf" $ \o ->
18+ MultipleOf <$> o .: "multipleOf"
19+
20+data MultipleOfInvalid
21+ = MultipleOfInvalid MultipleOf Scientific
22+ deriving (Eq, Show)
23+
24+-- | The spec requires @"multipleOf"@ to be positive.
25+multipleOfVal :: MultipleOf -> Scientific -> Maybe MultipleOfInvalid
26+multipleOfVal a@(MultipleOf n) x
27+ | n <= 0 = Nothing
28+ | x `mod'` n /= 0 = Just (MultipleOfInvalid a x)
29+ | otherwise = Nothing
30+
31+--------------------------------------------------
32+-- * maximum
33+--------------------------------------------------
34+
35+data Maximum = Maximum
36+ { _maximumExclusive :: Bool
37+ , _maximumValue :: Scientific
38+ } deriving (Eq, Show)
39+
40+instance FromJSON Maximum where
41+ parseJSON = withObject "Maximum" $ \o -> Maximum
42+ <$> o .:! "exclusiveMaximum" .!= False
43+ <*> o .: "maximum"
44+
45+data MaximumInvalid
46+ = MaximumInvalid Maximum Scientific
47+ deriving (Eq, Show)
48+
49+maximumVal
50+ :: Maximum
51+ -> Scientific
52+ -> Maybe MaximumInvalid
53+maximumVal a@(Maximum exclusive n) x
54+ | exclusive && x >= n = Just (MaximumInvalid a x)
55+ | x > n = Just (MaximumInvalid a x)
56+ | otherwise = Nothing
57+
58+--------------------------------------------------
59+-- * minimum
60+--------------------------------------------------
61+
62+data Minimum = Minimum
63+ { _minimumExclusive :: Bool
64+ , _minimumValue :: Scientific
65+ } deriving (Eq, Show)
66+
67+instance FromJSON Minimum where
68+ parseJSON = withObject "Minimum" $ \o -> Minimum
69+ <$> o .:! "exclusiveMinimum" .!= False
70+ <*> o .: "minimum"
71+
72+data MinimumInvalid
73+ = MinimumInvalid Minimum Scientific
74+ deriving (Eq, Show)
75+
76+minimumVal
77+ :: Minimum
78+ -> Scientific
79+ -> Maybe MinimumInvalid
80+minimumVal a@(Minimum exclusive n) x
81+ | exclusive && x <= n = Just (MinimumInvalid a x)
82+ | x < n = Just (MinimumInvalid a x)
83+ | otherwise = Nothing
src/JSONSchema/Validator/Draft4/Object.hsnew file+182
@@ -0,0 +1,182 @@
1+module JSONSchema.Validator.Draft4.Object
2+ ( module JSONSchema.Validator.Draft4.Object
3+ , module JSONSchema.Validator.Draft4.Object.Properties
4+ ) where
5+
6+import Import
7+
8+import qualified Data.HashMap.Strict as HM
9+import qualified Data.List.NonEmpty as NE
10+import Data.Set (Set)
11+import qualified Data.Set as Set
12+import qualified Data.Text as T
13+
14+import JSONSchema.Validator.Draft4.Object.Properties
15+import JSONSchema.Validator.Utils
16+
17+--------------------------------------------------
18+-- * maxProperties
19+--------------------------------------------------
20+
21+newtype MaxProperties
22+ = MaxProperties { _unMaxProperties :: Int }
23+ deriving (Eq, Show)
24+
25+instance FromJSON MaxProperties where
26+ parseJSON = withObject "MaxProperties" $ \o ->
27+ MaxProperties <$> o .: "maxProperties"
28+
29+data MaxPropertiesInvalid
30+ = MaxPropertiesInvalid MaxProperties (HashMap Text Value)
31+ deriving (Eq, Show)
32+
33+-- | The spec requires @"maxProperties"@ to be non-negative.
34+maxPropertiesVal
35+ :: MaxProperties
36+ -> HashMap Text Value
37+ -> Maybe MaxPropertiesInvalid
38+maxPropertiesVal a@(MaxProperties n) x
39+ | n < 0 = Nothing
40+ | HM.size x > n = Just (MaxPropertiesInvalid a x)
41+ | otherwise = Nothing
42+
43+--------------------------------------------------
44+-- * minProperties
45+--------------------------------------------------
46+
47+newtype MinProperties
48+ = MinProperties { _unMinProperties :: Int }
49+ deriving (Eq, Show)
50+
51+instance FromJSON MinProperties where
52+ parseJSON = withObject "MinProperties" $ \o ->
53+ MinProperties <$> o .: "minProperties"
54+
55+data MinPropertiesInvalid
56+ = MinPropertiesInvalid MinProperties (HashMap Text Value)
57+ deriving (Eq, Show)
58+
59+-- | The spec requires @"minProperties"@ to be non-negative.
60+minPropertiesVal
61+ :: MinProperties
62+ -> HashMap Text Value
63+ -> Maybe MinPropertiesInvalid
64+minPropertiesVal a@(MinProperties n) x
65+ | n < 0 = Nothing
66+ | HM.size x < n = Just (MinPropertiesInvalid a x)
67+ | otherwise = Nothing
68+
69+--------------------------------------------------
70+-- * required
71+--------------------------------------------------
72+
73+-- | From the spec:
74+--
75+-- > The value of this keyword MUST be an array.
76+-- > This array MUST have at least one element.
77+-- > Elements of this array MUST be strings, and MUST be unique.
78+newtype Required
79+ = Required { _unRequired :: Set Text }
80+ deriving (Eq, Show)
81+
82+instance FromJSON Required where
83+ parseJSON = withObject "Required" $ \o ->
84+ Required <$> o .: "required"
85+
86+instance Arbitrary Required where
87+ arbitrary = do
88+ x <- arbitraryText -- Guarantee at least one element.
89+ xs <- (fmap.fmap) T.pack arbitrary
90+ pure . Required . Set.fromList $ x:xs
91+
92+data RequiredInvalid
93+ = RequiredInvalid Required (Set Text) (HashMap Text Value)
94+ deriving (Eq, Show)
95+
96+requiredVal :: Required -> HashMap Text Value -> Maybe RequiredInvalid
97+requiredVal r@(Required ts) x
98+ | Set.null ts = Nothing
99+ | Set.null leftovers = Nothing
100+ | otherwise = Just (RequiredInvalid r leftovers x)
101+ where
102+ leftovers :: Set Text
103+ leftovers =
104+ Set.difference -- Items of the first set not in the second.
105+ ts
106+ (Set.fromList (HM.keys x))
107+
108+--------------------------------------------------
109+-- * dependencies
110+--------------------------------------------------
111+
112+newtype DependenciesValidator schema
113+ = DependenciesValidator
114+ { _unDependenciesValidator :: HashMap Text (Dependency schema) }
115+ deriving (Eq, Show)
116+
117+instance FromJSON schema => FromJSON (DependenciesValidator schema) where
118+ parseJSON = withObject "DependenciesValidator" $ \o ->
119+ DependenciesValidator <$> o .: "dependencies"
120+
121+data Dependency schema
122+ = SchemaDependency schema
123+ | PropertyDependency (Set Text)
124+ deriving (Eq, Show)
125+
126+instance FromJSON schema => FromJSON (Dependency schema) where
127+ parseJSON v = fmap SchemaDependency (parseJSON v)
128+ <|> fmap PropertyDependency (parseJSON v)
129+
130+instance ToJSON schema => ToJSON (Dependency schema) where
131+ toJSON (SchemaDependency schema) = toJSON schema
132+ toJSON (PropertyDependency ts) = toJSON ts
133+
134+instance Arbitrary schema => Arbitrary (Dependency schema) where
135+ arbitrary = oneof [ SchemaDependency <$> arbitrary
136+ , PropertyDependency <$> arbitrarySetOfText
137+ ]
138+
139+data DependencyMemberInvalid err
140+ = SchemaDepInvalid (NonEmpty err)
141+ | PropertyDepInvalid (Set Text) (HashMap Text Value)
142+ deriving (Eq, Show)
143+
144+newtype DependenciesInvalid err
145+ = DependenciesInvalid (HashMap Text (DependencyMemberInvalid err))
146+ deriving (Eq, Show)
147+
148+-- | From the spec:
149+-- <http://json-schema.org/latest/json-schema-validation.html#anchor70>
150+--
151+-- > This keyword's value MUST be an object.
152+-- > Each value of this object MUST be either an object or an array.
153+-- >
154+-- > If the value is an object, it MUST be a valid JSON Schema.
155+-- > This is called a schema dependency.
156+-- >
157+-- > If the value is an array, it MUST have at least one element.
158+-- > Each element MUST be a string, and elements in the array MUST be unique.
159+-- > This is called a property dependency.
160+dependenciesVal
161+ :: forall err schema.
162+ (schema -> Value -> [err])
163+ -> DependenciesValidator schema
164+ -> HashMap Text Value
165+ -> Maybe (DependenciesInvalid err)
166+dependenciesVal f (DependenciesValidator hm) x =
167+ let res = HM.mapMaybeWithKey g hm
168+ in if HM.null res
169+ then Nothing
170+ else Just (DependenciesInvalid res)
171+ where
172+ g :: Text -> Dependency schema -> Maybe (DependencyMemberInvalid err)
173+ g k (SchemaDependency schema)
174+ | HM.member k x = SchemaDepInvalid
175+ <$> NE.nonEmpty (f schema (Object x))
176+ | otherwise = Nothing
177+ g k (PropertyDependency ts)
178+ | HM.member k x && not allPresent = Just (PropertyDepInvalid ts x)
179+ | otherwise = Nothing
180+ where
181+ allPresent :: Bool
182+ allPresent = all (`HM.member` x) ts
src/JSONSchema/Validator/Draft4/Object/Properties.hsnew file+209
@@ -0,0 +1,209 @@
1+module JSONSchema.Validator.Draft4.Object.Properties where
2+
3+import Import
4+
5+import qualified Data.Hashable as HA
6+import qualified Data.HashMap.Strict as HM
7+import qualified Data.List.NonEmpty as NE
8+import Data.Text.Encoding (encodeUtf8)
9+import qualified JSONPointer as JP
10+import qualified Text.Regex.PCRE.Heavy as RE
11+
12+data PropertiesRelated schema = PropertiesRelated
13+ { _propProperties :: Maybe (HashMap Text schema)
14+ -- ^ 'Maybe' is used to distinguish whether the key is present or not.
15+ , _propPattern :: Maybe (HashMap Text schema)
16+ , _propAdditional :: Maybe (AdditionalProperties schema)
17+ } deriving (Eq, Show)
18+
19+instance FromJSON schema => FromJSON (PropertiesRelated schema) where
20+ parseJSON = withObject "PropertiesRelated" $ \o -> PropertiesRelated
21+ <$> o .:! "properties"
22+ <*> o .:! "patternProperties"
23+ <*> o .:! "additionalProperties"
24+
25+emptyProperties :: PropertiesRelated schema
26+emptyProperties = PropertiesRelated
27+ { _propProperties = Nothing
28+ , _propPattern = Nothing
29+ , _propAdditional = Nothing
30+ }
31+
32+data AdditionalProperties schema
33+ = AdditionalPropertiesBool Bool
34+ | AdditionalPropertiesObject schema
35+ deriving (Eq, Show)
36+
37+instance FromJSON schema => FromJSON (AdditionalProperties schema) where
38+ parseJSON v = fmap AdditionalPropertiesBool (parseJSON v)
39+ <|> fmap AdditionalPropertiesObject (parseJSON v)
40+
41+instance ToJSON schema => ToJSON (AdditionalProperties schema) where
42+ toJSON (AdditionalPropertiesBool b) = toJSON b
43+ toJSON (AdditionalPropertiesObject hm) = toJSON hm
44+
45+instance Arbitrary schema => Arbitrary (AdditionalProperties schema) where
46+ arbitrary = oneof [ AdditionalPropertiesBool <$> arbitrary
47+ , AdditionalPropertiesObject <$> arbitrary
48+ ]
49+
50+-- | A glorified @type@ alias.
51+newtype Regex
52+ = Regex { _unRegex :: Text }
53+ deriving (Eq, Show, Generic)
54+
55+instance HA.Hashable Regex
56+
57+-- NOTE: We'd like to enforce that at least one error exists here.
58+data PropertiesRelatedInvalid err = PropertiesRelatedInvalid
59+ { _prInvalidProperties :: HashMap Text [err]
60+ , _prInvalidPattern :: HashMap (Regex, JP.Key) [err]
61+ , _prInvalidAdditional :: Maybe (APInvalid err)
62+ } deriving (Eq, Show)
63+
64+data APInvalid err
65+ = APBoolInvalid (HashMap Text Value)
66+ | APObjectInvalid (HashMap Text (NonEmpty err))
67+ deriving (Eq, Show)
68+
69+-- | First @"properties"@ and @"patternProperties"@ are run simultaneously
70+-- on the data, then @"additionalProperties"@ is run on the remainder.
71+propertiesRelatedVal
72+ :: forall err schema.
73+ (schema -> Value -> [err])
74+ -> PropertiesRelated schema
75+ -> HashMap Text Value
76+ -> Maybe (PropertiesRelatedInvalid err)
77+propertiesRelatedVal f props x
78+ | all null (HM.elems propFailures)
79+ && all null (HM.elems patFailures)
80+ && isNothing addFailures = Nothing
81+ | otherwise =
82+ Just PropertiesRelatedInvalid
83+ { _prInvalidProperties = propFailures
84+ , _prInvalidPattern = patFailures
85+ , _prInvalidAdditional = addFailures
86+ }
87+ where
88+ propertiesHm :: HashMap Text schema
89+ propertiesHm = fromMaybe mempty (_propProperties props)
90+
91+ patHm :: HashMap Text schema
92+ patHm = fromMaybe mempty (_propPattern props)
93+
94+ propAndUnmatched :: (HashMap Text [err], Remaining)
95+ propAndUnmatched = ( HM.intersectionWith f propertiesHm x
96+ , Remaining (HM.difference x propertiesHm)
97+ )
98+
99+ (propFailures, propRemaining) = propAndUnmatched
100+
101+ patAndUnmatched :: (HashMap (Regex, JP.Key) [err], Remaining)
102+ patAndUnmatched = patternAndUnmatched f patHm x
103+
104+ (patFailures, patRemaining) = patAndUnmatched
105+
106+ finalRemaining :: Remaining
107+ finalRemaining = Remaining (HM.intersection (_unRemaining patRemaining)
108+ (_unRemaining propRemaining))
109+
110+ addFailures :: Maybe (APInvalid err)
111+ addFailures = (\addProp -> additionalProperties f addProp finalRemaining)
112+ =<< _propAdditional props
113+
114+-- | Internal.
115+newtype Remaining
116+ = Remaining { _unRemaining :: HashMap Text Value }
117+
118+-- | Internal.
119+patternAndUnmatched
120+ :: forall err schema.
121+ (schema -> Value -> [err])
122+ -> HashMap Text schema
123+ -> HashMap Text Value
124+ -> (HashMap (Regex, JP.Key) [err], Remaining)
125+patternAndUnmatched f patPropertiesHm x =
126+ (HM.foldlWithKey' runMatches mempty perhapsMatches, remaining)
127+ where
128+ -- @[(Regex, schema)]@ will have one item per match.
129+ perhapsMatches :: HashMap Text ([(Regex, schema)], Value)
130+ perhapsMatches =
131+ HM.foldlWithKey' (matchingSchemas patPropertiesHm) mempty x
132+ where
133+ matchingSchemas
134+ :: HashMap Text schema
135+ -> HashMap Text ([(Regex, schema)], Value)
136+ -> Text
137+ -> Value
138+ -> HashMap Text ([(Regex, schema)], Value)
139+ matchingSchemas subSchemas acc k v =
140+ HM.insert k
141+ (HM.foldlWithKey' (checkKey k) mempty subSchemas, v)
142+ acc
143+
144+ checkKey
145+ :: Text
146+ -> [(Regex, schema)]
147+ -> Text
148+ -> schema
149+ -> [(Regex, schema)]
150+ checkKey k acc r subSchema =
151+ case RE.compileM (encodeUtf8 r) mempty of
152+ Left _ -> acc
153+ Right re -> if k RE.=~ re
154+ then (Regex r, subSchema) : acc
155+ else acc
156+
157+ runMatches
158+ :: HashMap (Regex, JP.Key) [err]
159+ -> Text
160+ -> ([(Regex, schema)], Value)
161+ -> HashMap (Regex, JP.Key) [err]
162+ runMatches acc k (matches,v) =
163+ foldr runMatch acc matches
164+ where
165+ runMatch
166+ :: (Regex, schema)
167+ -> HashMap (Regex, JP.Key) [err]
168+ -> HashMap (Regex, JP.Key) [err]
169+ runMatch (r,schema) = HM.insert (r, JP.Key k) (f schema v)
170+
171+ remaining :: Remaining
172+ remaining = Remaining . fmap snd . HM.filter (null . fst) $ perhapsMatches
173+
174+-- Internal.
175+additionalProperties
176+ :: forall err schema.
177+ (schema -> Value -> [err])
178+ -> AdditionalProperties schema
179+ -> Remaining
180+ -> Maybe (APInvalid err)
181+additionalProperties f a x =
182+ case a of
183+ AdditionalPropertiesBool b ->
184+ APBoolInvalid <$> additionalPropertiesBool b x
185+ AdditionalPropertiesObject b ->
186+ APObjectInvalid <$> additionalPropertiesObject f b x
187+
188+-- | Internal.
189+additionalPropertiesBool
190+ :: Bool
191+ -> Remaining
192+ -> Maybe (HashMap Text Value)
193+additionalPropertiesBool True _ = Nothing
194+additionalPropertiesBool False (Remaining x)
195+ | HM.size x > 0 = Just x
196+ | otherwise = Nothing
197+
198+-- | Internal.
199+additionalPropertiesObject
200+ :: forall err schema.
201+ (schema -> Value -> [err])
202+ -> schema
203+ -> Remaining
204+ -> Maybe (HashMap Text (NonEmpty err))
205+additionalPropertiesObject f schema (Remaining x) =
206+ let errs = HM.mapMaybe (NE.nonEmpty . f schema) x
207+ in if HM.null errs
208+ then Nothing
209+ else Just errs
src/JSONSchema/Validator/Draft4/String.hsnew file+78
@@ -0,0 +1,78 @@
1+module JSONSchema.Validator.Draft4.String where
2+
3+import Import
4+
5+import qualified Data.Text as T
6+import Data.Text.Encoding (encodeUtf8)
7+import qualified Text.Regex.PCRE.Heavy as RE
8+
9+--------------------------------------------------
10+-- * maxLength
11+--------------------------------------------------
12+
13+newtype MaxLength
14+ = MaxLength { _unMaxLength :: Int }
15+ deriving (Eq, Show)
16+
17+instance FromJSON MaxLength where
18+ parseJSON = withObject "MaxLength" $ \o ->
19+ MaxLength <$> o .: "maxLength"
20+
21+data MaxLengthInvalid
22+ = MaxLengthInvalid MaxLength Text
23+ deriving (Eq, Show)
24+
25+-- | The spec requires @"maxLength"@ to be non-negative.
26+maxLengthVal :: MaxLength -> Text -> Maybe MaxLengthInvalid
27+maxLengthVal a@(MaxLength n) x
28+ | n <= 0 = Nothing
29+ | T.length x > n = Just (MaxLengthInvalid a x)
30+ | otherwise = Nothing
31+
32+--------------------------------------------------
33+-- * minLength
34+--------------------------------------------------
35+
36+newtype MinLength
37+ = MinLength { _unMinLength :: Int }
38+ deriving (Eq, Show)
39+
40+instance FromJSON MinLength where
41+ parseJSON = withObject "MinLength" $ \o ->
42+ MinLength <$> o .: "minLength"
43+
44+data MinLengthInvalid
45+ = MinLengthInvalid MinLength Text
46+ deriving (Eq, Show)
47+
48+-- | The spec requires @"minLength"@ to be non-negative.
49+minLengthVal :: MinLength -> Text -> Maybe MinLengthInvalid
50+minLengthVal a@(MinLength n) x
51+ | n <= 0 = Nothing
52+ | T.length x < n = Just (MinLengthInvalid a x)
53+ | otherwise = Nothing
54+
55+--------------------------------------------------
56+-- * pattern
57+--------------------------------------------------
58+
59+newtype PatternValidator
60+ = PatternValidator { _unPatternValidator :: Text }
61+ deriving (Eq, Show)
62+
63+instance FromJSON PatternValidator where
64+ parseJSON = withObject "PatternValidator" $ \o ->
65+ PatternValidator <$> o .: "pattern"
66+
67+data PatternInvalid
68+ = PatternNotRegex -- TODO: let these pass successfully?
69+ | PatternInvalid PatternValidator Text
70+ deriving (Eq, Show)
71+
72+patternVal :: PatternValidator -> Text -> Maybe PatternInvalid
73+patternVal a@(PatternValidator t) x =
74+ case RE.compileM (encodeUtf8 t) mempty of
75+ Left _ -> Just PatternNotRegex
76+ Right re -> if x RE.=~ re
77+ then Nothing
78+ else Just (PatternInvalid a x)
src/JSONSchema/Validator/Reference.hsnew file+71
@@ -0,0 +1,71 @@
1+-- | JSON Reference is described here:
2+-- <http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03>
3+--
4+-- And is extended for JSON Schema here:
5+-- <http://json-schema.org/latest/json-schema-core.html#anchor26>
6+module JSONSchema.Validator.Reference where
7+
8+import Import
9+
10+import qualified Data.Text as T
11+import System.FilePath (dropFileName, (</>))
12+
13+data Scope schema = Scope
14+ { _topLevelDocument :: schema
15+ , _documentURI :: Maybe Text
16+ , _currentBaseURI :: BaseURI
17+ } deriving (Eq, Show)
18+
19+newtype BaseURI
20+ = BaseURI { _unBaseURI :: Maybe Text }
21+ deriving (Eq, Show)
22+
23+-- | TODO: no `type`s.
24+type URIAndFragment = (Maybe Text, Maybe Text)
25+
26+updateResolutionScope :: BaseURI -> Maybe Text -> BaseURI
27+updateResolutionScope base idKeyword =
28+ case idKeyword of
29+ Just t -> BaseURI . fst . baseAndFragment $ resolveScopeAgainst base t
30+ Nothing -> base
31+
32+resolveReference :: BaseURI -> Text -> URIAndFragment
33+resolveReference base t = baseAndFragment (resolveScopeAgainst base t)
34+
35+--------------------------------------------------
36+-- * Helpers
37+--------------------------------------------------
38+
39+isRemoteReference :: Text -> Bool
40+isRemoteReference = T.isInfixOf "://"
41+
42+baseAndFragment :: Text -> URIAndFragment
43+baseAndFragment = f . T.splitOn "#"
44+ where
45+ f :: [Text] -> URIAndFragment
46+ f [x] = (g x, Nothing)
47+ f [x,y] = (g x, g y)
48+ f _ = (Nothing, Nothing)
49+
50+ g "" = Nothing
51+ g x = Just x
52+
53+resolveScopeAgainst :: BaseURI -> Text -> Text
54+resolveScopeAgainst (BaseURI Nothing) t = t
55+resolveScopeAgainst (BaseURI (Just base)) t
56+ | isRemoteReference t = t
57+ | otherwise = smartAppend
58+ where
59+ -- There shouldn't be a fragment at the end of a scope URI,
60+ -- but just in case a user leaves one in we want to be sure
61+ -- to cut it off before appending.
62+ smartAppend :: Text
63+ smartAppend =
64+ case baseAndFragment base of
65+ (Just uri,_) ->
66+ case T.unpack t of
67+ -- We want "/foo" and "#/bar" to combine into
68+ -- "/foo#/bar" not "/foo/#/bar".
69+ '#':_ -> base <> t
70+ _ -> T.pack (dropFileName (T.unpack uri) </> T.unpack t)
71+ _ -> t
src/JSONSchema/Validator/Types.hsnew file+22
@@ -0,0 +1,22 @@
1+module JSONSchema.Validator.Types where
2+
3+import Import
4+
5+import Data.Profunctor (Profunctor(..))
6+
7+data Validator schema val err = Validator
8+ { _embedded :: val -> ([schema], [schema])
9+ -- ^ The first list is embedded schemas that validate the same piece
10+ -- of data this schema validates (such as schemas embedded in 'allOf').
11+ -- The second is embedded schemas that validate a subset of that data
12+ -- (such as the schemas embedded in 'items').
13+ --
14+ -- This is done to allow static detection of loops, though this isn't
15+ -- implemented yet (though the Draft4 code does do live loop detection
16+ -- during validation).
17+ , _validate :: val -> Value -> [err]
18+ } deriving Functor
19+
20+instance Profunctor (Validator schema) where
21+ lmap f (Validator a b) = Validator (lmap f a) (lmap f b)
22+ rmap = fmap
src/JSONSchema/Validator/Utils.hsnew file+119
@@ -0,0 +1,119 @@
1+module JSONSchema.Validator.Utils where
2+
3+import Import
4+
5+import Control.Monad (fail)
6+import qualified Data.HashMap.Strict as HM
7+import qualified Data.List.NonEmpty as NE
8+import Data.Scientific (Scientific, fromFloatDigits)
9+import Data.Set (Set)
10+import qualified Data.Set as S
11+import qualified Data.Text as T
12+import qualified Data.Vector as V
13+
14+--------------------------------------------------
15+-- * QuickCheck
16+--------------------------------------------------
17+
18+arbitraryText :: Gen Text
19+arbitraryText = T.pack <$> arbitrary
20+
21+arbitraryScientific :: Gen Scientific
22+arbitraryScientific = (fromFloatDigits :: Double -> Scientific) <$> arbitrary
23+
24+arbitraryPositiveScientific :: Gen Scientific
25+arbitraryPositiveScientific = (fromFloatDigits :: Double -> Scientific)
26+ . getPositive
27+ <$> arbitrary
28+
29+newtype ArbitraryValue
30+ = ArbitraryValue { _unArbitraryValue :: Value }
31+ deriving (Eq, Show)
32+
33+instance Arbitrary ArbitraryValue where
34+ arbitrary = ArbitraryValue <$> sized f
35+ where
36+ f :: Int -> Gen Value
37+ f n | n <= 1 = oneof nonRecursive
38+ | otherwise = oneof $
39+ fmap (Array . V.fromList) (traverse (const (f (n `div` 10)))
40+ =<< (arbitrary :: Gen [()]))
41+ : fmap (Object . HM.fromList) (traverse (const (g (n `div` 10)))
42+ =<< (arbitrary :: Gen [()]))
43+ : nonRecursive
44+
45+ g :: Int -> Gen (Text, Value)
46+ g n = (,) <$> arbitraryText <*> f n
47+
48+ nonRecursive :: [Gen Value]
49+ nonRecursive =
50+ [ pure Null
51+ , Bool <$> arbitrary
52+ , String <$> arbitraryText
53+ , Number <$> arbitraryScientific
54+ ]
55+
56+arbitraryHashMap :: Arbitrary a => Gen (HashMap Text a)
57+arbitraryHashMap = HM.fromList . fmap (first T.pack) <$> arbitrary
58+
59+arbitrarySetOfText :: Gen (Set Text)
60+arbitrarySetOfText = S.fromList . fmap T.pack <$> arbitrary
61+
62+newtype NonEmpty' a = NonEmpty' { _unNonEmpty' :: NonEmpty a }
63+
64+instance FromJSON a => FromJSON (NonEmpty' a) where
65+ parseJSON v = do
66+ xs <- parseJSON v
67+ case NE.nonEmpty xs of
68+ Nothing -> fail "Must have at least one item."
69+ Just ne -> pure (NonEmpty' ne)
70+
71+instance ToJSON a => ToJSON (NonEmpty' a) where
72+ toJSON = toJSON . NE.toList . _unNonEmpty'
73+
74+instance Arbitrary a => Arbitrary (NonEmpty' a) where
75+ arbitrary = do
76+ xs <- arbitrary
77+ case NE.nonEmpty xs of
78+ Nothing -> NonEmpty' . pure <$> arbitrary
79+ Just ne -> pure (NonEmpty' ne)
80+
81+--------------------------------------------------
82+-- * allUniqueValues
83+--------------------------------------------------
84+
85+allUniqueValues :: (Foldable f, Functor f) => f Value -> Bool
86+allUniqueValues = allUnique . fmap OrdValue
87+
88+allUnique :: (Foldable f, Ord a) => f a -> Bool
89+allUnique xs = S.size (S.fromList (toList xs)) == length xs
90+
91+-- | OrdValue's Ord instance needs benchmarking, but it allows us to
92+-- use our 'allUnique' function instead of O(n^2) nub, so it's probably
93+-- worth it.
94+newtype OrdValue = OrdValue { _unOrdValue :: Value } deriving Eq
95+
96+instance Ord OrdValue where
97+ (OrdValue Null) `compare` (OrdValue Null) = EQ
98+ (OrdValue Null) `compare` _ = LT
99+ _ `compare` (OrdValue Null) = GT
100+
101+ (OrdValue (Bool x)) `compare` (OrdValue (Bool y)) = x `compare` y
102+ (OrdValue (Bool _)) `compare` _ = LT
103+ _ `compare` (OrdValue (Bool _)) = GT
104+
105+ (OrdValue (Number x)) `compare` (OrdValue (Number y)) = x `compare` y
106+ (OrdValue (Number _)) `compare` _ = LT
107+ _ `compare` (OrdValue (Number _)) = GT
108+
109+ (OrdValue (String x)) `compare` (OrdValue (String y)) = x `compare` y
110+ (OrdValue (String _)) `compare` _ = LT
111+ _ `compare` (OrdValue (String _)) = GT
112+
113+ (OrdValue (Array xs)) `compare` (OrdValue (Array ys)) =
114+ (OrdValue <$> xs) `compare` (OrdValue <$> ys)
115+ (OrdValue (Array _)) `compare` _ = LT
116+ _ `compare` (OrdValue (Array _)) = GT
117+
118+ (OrdValue (Object x)) `compare` (OrdValue (Object y)) =
119+ HM.toList (OrdValue <$> x) `compare` HM.toList (OrdValue <$> y)
draft4.json → src/draft4.jsonrenamed

The content is the same.

stack.yamlnew file+2
@@ -0,0 +1,2 @@
1+resolver: lts-10.8
2+pvp-bounds: upper-revision
test/Local.hsnew file+98
@@ -0,0 +1,98 @@
1+module Main where
2+
3+import Protolude
4+
5+import Data.Aeson
6+import qualified Data.List.NonEmpty as NE
7+import qualified System.Timeout as TO
8+import Test.Hspec
9+import Test.QuickCheck (property)
10+
11+import qualified JSONSchema.Draft4 as D4
12+import qualified JSONSchema.Types as JT
13+import qualified Local.Failure
14+import qualified Local.Reference
15+import qualified Local.Validation
16+import Shared
17+
18+-- Examples
19+import qualified AlternateSchema
20+import qualified Simple
21+import qualified TwoStep
22+
23+dir :: FilePath
24+dir = "JSON-Schema-Test-Suite/tests/draft4"
25+
26+supplementDir :: FilePath
27+supplementDir = "test/supplement"
28+
29+main :: IO ()
30+main = do
31+
32+ -- Language agnostic tests
33+ ts <- readSchemaTests
34+ dir
35+ (\a -> not (isHTTPTest a || skipTest a))
36+
37+ -- Custom supplements to the language agnostic tests
38+ supplementTs <- readSchemaTests supplementDir (not . isHTTPTest)
39+
40+ hspec $ do
41+ describe "Examples" exampleTests
42+ describe "QuickCheck" quickCheckTests
43+ describe "Failure" Local.Failure.spec
44+ describe "JSONSchema.Validator.Reference" Local.Reference.spec
45+ describe "Supplementary validation tests written in Haskell"
46+ Local.Validation.spec
47+
48+ describe "Supplementary tests written in JSON (using the record based schema)"
49+ (traverse_ (toTest (fmap timeout . validate)) supplementTs)
50+ describe "Supplementary tests written in JSON (using the 'Value' based schema)"
51+ (traverse_ (toTest (fmap timeout . validateExample)) supplementTs)
52+
53+ describe "Language agnostic tests (using the record based schema)"
54+ (traverse_ (toTest validate) ts)
55+ describe "Language agnostic tests (using the 'Value' based schema)"
56+ (traverse_ (toTest validateExample) ts)
57+ where
58+ timeout :: Expectation -> Expectation
59+ timeout f = do
60+ res <- TO.timeout 3000000 f
61+ case res of
62+ Nothing -> expectationFailure "timeout expired"
63+ Just a -> pure a
64+
65+ validate :: D4.Schema -> SchemaTestCase -> Expectation
66+ validate s sc = do
67+ res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
68+ let failures = case res of
69+ Right () -> mempty
70+ Left (D4.HVData a) -> NE.toList (D4._invalidFailures a)
71+ other -> panic ("Local.validate error: "
72+ <> show other)
73+ assertResult sc failures
74+
75+ validateExample :: JT.Schema -> SchemaTestCase -> Expectation
76+ validateExample s sc = do
77+ res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
78+ case res of
79+ Left e -> panic ("Local.validateExample error: " <> show e)
80+ Right schemaMap -> do
81+ let failures = AlternateSchema.validate
82+ schemaMap
83+ (D4.SchemaWithURI s Nothing)
84+ (_scData sc)
85+ assertResult sc failures
86+
87+quickCheckTests :: Spec
88+quickCheckTests =
89+ it "schemas invert through JSON without change" $ do
90+ property invertSchema
91+ where
92+ invertSchema :: D4.Schema -> Bool
93+ invertSchema a = Just a == decode (encode a)
94+
95+exampleTests :: Spec
96+exampleTests = do
97+ it "Simple.example compiles successfully" Simple.example
98+ it "TwoStep.example compiles successfully" TwoStep.example
test/Local/Failure.hsnew file+105
@@ -0,0 +1,105 @@
1+module Local.Failure where
2+
3+import Protolude
4+
5+import Data.Aeson
6+import qualified Data.Vector as V
7+import qualified JSONPointer as JP
8+import Test.Hspec
9+
10+import JSONSchema.Draft4
11+import qualified JSONSchema.Draft4.Spec as Spec
12+import qualified JSONSchema.Validator.Draft4 as VAL
13+
14+spec :: Spec
15+spec = do
16+ it "items array failures are constructed correctly" itemsArray
17+ it "items object failures are constructed correctly" itemsObject
18+ it "reports infinitely looping references correctly" loopRef
19+
20+itemsArray :: Expectation
21+itemsArray =
22+ failures `shouldBe`
23+ [ FailureItems (VAL.ItemsArrayInvalid (
24+ pure ( JP.Index 0
25+ , pure (FailureUniqueItems (VAL.UniqueItemsInvalid
26+ (V.fromList [Null, Null])
27+ ))
28+ )
29+ ))
30+ ]
31+ where
32+ failures :: [ValidatorFailure]
33+ failures = Spec.specValidate mempty sw badData
34+
35+ schema :: Schema
36+ schema = emptySchema
37+ { _schemaItems = Just $ VAL.ItemsArray
38+ [emptySchema { _schemaUniqueItems = Just True }]
39+ }
40+
41+ sw :: SchemaWithURI Schema
42+ sw = SchemaWithURI
43+ { _swSchema = schema
44+ , _swURI = Nothing
45+ }
46+
47+ badData :: Value
48+ badData = toJSON [[Null, Null]]
49+
50+itemsObject :: Expectation
51+itemsObject =
52+ failures `shouldBe`
53+ [ FailureItems (VAL.ItemsObjectInvalid (
54+ pure ( JP.Index 1
55+ , pure (FailureUniqueItems (VAL.UniqueItemsInvalid
56+ (V.fromList [Null, Null])
57+ ))
58+ )
59+ ))
60+ ]
61+ where
62+ failures :: [ValidatorFailure]
63+ failures = Spec.specValidate mempty sw badData
64+
65+ schema :: Schema
66+ schema = emptySchema
67+ { _schemaItems = Just $ VAL.ItemsObject
68+ (emptySchema { _schemaUniqueItems = Just True })
69+ }
70+
71+ sw :: SchemaWithURI Schema
72+ sw = SchemaWithURI
73+ { _swSchema = schema
74+ , _swURI = Nothing
75+ }
76+
77+ badData :: Value
78+ badData = toJSON [Null, toJSON [Null, Null]]
79+
80+loopRef :: Expectation
81+loopRef =
82+ failures `shouldBe`
83+ [ FailureRef (VAL.RefLoop
84+ "#"
85+ (VAL.VisitedSchemas [(Nothing, Nothing)])
86+ (Nothing, Nothing)
87+ )
88+ ]
89+ where
90+ failures :: [ValidatorFailure]
91+ failures = Spec.specValidate mempty sw badData
92+
93+ schema :: Schema
94+ schema = emptySchema
95+ { _schemaRef = Just "#"
96+ }
97+
98+ sw :: SchemaWithURI Schema
99+ sw = SchemaWithURI
100+ { _swSchema = schema
101+ , _swURI = Nothing
102+ }
103+
104+ badData :: Value
105+ badData = toJSON [[Null, Null]]
test/Local/Reference.hsnew file+36
@@ -0,0 +1,36 @@
1+module Local.Reference where
2+
3+import Protolude
4+
5+import Test.Hspec
6+
7+import JSONSchema.Validator.Reference
8+
9+spec :: Spec
10+spec = do
11+ it "updateResolutionScope gives correct results" $ do
12+ updateResolutionScope (BaseURI Nothing) Nothing
13+ `shouldBe` BaseURI Nothing
14+
15+ updateResolutionScope (BaseURI Nothing) (Just "#")
16+ `shouldBe` BaseURI Nothing
17+
18+ updateResolutionScope (BaseURI Nothing) (Just "foo")
19+ `shouldBe` BaseURI (Just "foo")
20+
21+ -- TODO: Normalize after updateResolutionScope:
22+ updateResolutionScope (BaseURI (Just "/foo")) (Just "./bar")
23+ `shouldBe` BaseURI (Just "/./bar")
24+
25+ it "resolveReference gives correct results" $ do
26+ resolveReference (BaseURI (Just "/foo")) "bar"
27+ `shouldBe` (Just "/bar", Nothing)
28+
29+ resolveReference (BaseURI (Just "/foo/bar")) "/baz"
30+ `shouldBe` (Just "/baz", Nothing)
31+
32+ resolveReference (BaseURI Nothing) "#/bar"
33+ `shouldBe` (Nothing, Just "/bar")
34+
35+ resolveReference (BaseURI (Just "/foo")) "#/bar"
36+ `shouldBe` (Just "/foo", Just "/bar")
test/Local/Validation.hsnew file+70
@@ -0,0 +1,70 @@
1+module Local.Validation where
2+
3+import Protolude
4+
5+import Data.Aeson
6+import qualified Data.Aeson as AE
7+import qualified Data.HashMap.Strict as HM
8+import Data.String (String)
9+import Test.Hspec
10+
11+import qualified AlternateSchema as AS
12+import JSONSchema.Draft4
13+import qualified JSONSchema.Types as JT
14+
15+spec :: Spec
16+spec = do
17+ -- Fetching from filesystem.
18+
19+ it "a readFile exception during validation is turned into a left"
20+ readFileException
21+ it "Relative reference to local file"
22+ (resolutionInvalid "test/Local/schema.json")
23+ it "Chained relative references to local files"
24+ (resolutionInvalid "./test/Local/schema-with-ref.json")
25+
26+ -- Validation tests that were hard to write in JSON for whatever reason.
27+
28+ it "Don't parse schemas that have Null in a forbidden location"
29+ forbidNull
30+ it "The 'Value' based Schema's checkSchema should catch Nulls"
31+ exampleForbidNull
32+
33+readFileException :: Expectation
34+readFileException = do
35+ let schema = emptySchema { _schemaRef = Just "does-not-exist.json" }
36+ res <- referencesViaFilesystem (SchemaWithURI schema Nothing)
37+ case res of
38+ Left (FSReadFailure _) -> pure ()
39+ a -> expectationFailure (msg <> show a)
40+ where
41+ msg :: String
42+ msg = "expected referencesViaFilesystem to return ReadFailure,"
43+ <> " instead got: "
44+
45+resolutionInvalid :: Text -> Expectation
46+resolutionInvalid ref = do
47+ let schema = emptySchema { _schemaRef = Just ref }
48+ res <- fetchFilesystemAndValidate (SchemaWithURI schema Nothing) badData
49+ case res of
50+ Left (FVData _) -> pure ()
51+ a -> expectationFailure (msg <> show a)
52+ where
53+ badData :: Value
54+ badData = toJSON [True, True]
55+
56+ msg :: String
57+ msg = "expected fetchFilesystemAndValidate to return"
58+ <> " Left (FVData [_]), instead got: "
59+
60+forbidNull :: Expectation
61+forbidNull =
62+ case fromJSON (Object (HM.singleton "type" Null)) of
63+ AE.Error _ -> pure ()
64+ AE.Success a -> expectationFailure ("parsed to: " <> show (a :: Schema))
65+
66+exampleForbidNull :: Expectation
67+exampleForbidNull =
68+ case AS.checkSchema (JT.Schema (HM.singleton "type" Null)) of
69+ [] -> expectationFailure "No checkSchema failures"
70+ _ -> pure ()
test/Local/schema-with-ref.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "$ref": "./schema.json"
3+}
test/Local/schema.jsonnew file+3
@@ -0,0 +1,3 @@
1+{
2+ "uniqueItems": true
3+}
test/Remote.hsnew file+57
@@ -0,0 +1,57 @@
1+module Main where
2+
3+import Protolude
4+
5+import Control.Concurrent.Async (withAsync)
6+import qualified Data.List.NonEmpty as NE
7+import Network.Wai.Application.Static (defaultFileServerSettings,
8+ staticApp)
9+import qualified Network.Wai.Handler.Warp as Warp
10+import Test.Hspec
11+
12+import qualified JSONSchema.Draft4 as D4
13+import qualified JSONSchema.Types as JT
14+import Shared
15+
16+-- Examples
17+import qualified AlternateSchema
18+
19+dir :: FilePath
20+dir = "JSON-Schema-Test-Suite/tests/draft4"
21+
22+main :: IO ()
23+main = withAsync serve $ \_ -> do
24+ ts <- readSchemaTests dir isHTTPTest
25+ hspec $ do
26+ describe "Language agnostic tests (using the record based schema)"
27+ (traverse_ (toTest validate) ts)
28+ describe "Language agnostic tests (using the 'Value' based schema)"
29+ (traverse_ (toTest validateExample) ts)
30+ where
31+ validate :: D4.Schema -> SchemaTestCase -> Expectation
32+ validate s sc = do
33+ res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)
34+ let failures = case res of
35+ Right () -> mempty
36+ Left (D4.HVData a) -> NE.toList (D4._invalidFailures a)
37+ other -> panic ("Local.validate error: "
38+ <> show other)
39+ assertResult sc failures
40+
41+ validateExample :: JT.Schema -> SchemaTestCase -> Expectation
42+ validateExample s sc = do
43+ res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)
44+ case res of
45+ Left e -> panic ("Remote.validateExample error: " <> show e)
46+ Right schemaMap -> do
47+ let failures = AlternateSchema.validate
48+ schemaMap
49+ (D4.SchemaWithURI s Nothing)
50+ (_scData sc)
51+ assertResult sc failures
52+
53+serve :: IO ()
54+serve = Warp.run 1234
55+ . staticApp
56+ . defaultFileServerSettings
57+ $ "JSON-Schema-Test-Suite/remotes"
test/Shared.hsnew file+134
@@ -0,0 +1,134 @@
1+module Shared where
2+
3+import Protolude
4+
5+import Control.Monad (fail)
6+import Data.Aeson
7+import Data.Aeson.TH (fieldLabelModifier)
8+import qualified Data.ByteString as BS
9+import Data.Char (toLower)
10+import Data.List (stripPrefix, unlines)
11+import qualified Data.Text as T
12+import qualified System.Directory as SD
13+import System.FilePath ((</>))
14+import Test.Hspec
15+
16+skipTest :: FilePath -> Bool
17+skipTest file = (file == "optional/format.json") -- Optional
18+ || (file == "optional/zeroTerminatedFloats.json")
19+ || (file == "optional/ecmascript-regex.json")
20+
21+
22+isHTTPTest :: FilePath -> Bool
23+isHTTPTest file = (file == "definitions.json")
24+ || (file == "ref.json")
25+ || (file == "refRemote.json")
26+
27+-- Recursively return the contents of a directory
28+-- (or return itself if given a file as an argument).
29+--
30+-- Return paths are relative to that directory.
31+listFiles :: FilePath -> IO [FilePath]
32+listFiles path = fmap ( fromMaybe "stripPrefix failed"
33+ . stripPrefix (path <> "/")
34+ ) <$> listFilesFullPath path
35+
36+-- Recursively return the contents of a directory
37+-- (or return itself if given a file as an argument).
38+--
39+-- All return paths start with the 'FilePath' argument.
40+listFilesFullPath :: FilePath -> IO [FilePath]
41+listFilesFullPath path = do
42+ -- Check if it's a file or directory:
43+ res <- SD.doesFileExist path
44+ if res
45+ then pure [path]
46+ else do
47+ fs <- fmap (path </>) <$> SD.listDirectory path
48+ concat <$> traverse listFilesFullPath fs
49+
50+data SchemaTest = SchemaTest
51+ { _stDescription :: Text
52+ , _stSchema :: Value
53+ , _stCases :: [SchemaTestCase]
54+ }
55+
56+data SchemaTestCase = SchemaTestCase
57+ { _scDescription :: Text
58+ , _scData :: Value
59+ , _scValid :: Bool
60+ } deriving Generic
61+
62+instance FromJSON SchemaTest where
63+ parseJSON = withObject "SchemaTest" $ \o -> SchemaTest
64+ <$> o .: "description"
65+ <*> o .: "schema"
66+ <*> o .: "tests" -- Perhaps "cases" would have been
67+ -- a more descriptive key.
68+
69+instance FromJSON SchemaTestCase where
70+ parseJSON = genericParseJSON
71+ defaultOptions
72+ { fieldLabelModifier = fmap toLower . drop 3 }
73+
74+readSchemaTests
75+ :: FilePath
76+ -- ^ The path to a directory.
77+ -> (FilePath -> Bool)
78+ -- ^ A function to decide what we're interested in within that directory.
79+ -> IO [SchemaTest]
80+readSchemaTests dir filterFunc = do
81+ files <- filter filterFunc <$> listFiles dir
82+ concat <$> traverse fileToCases files
83+ where
84+ -- Each file contains an array of SchemaTests, not just one.
85+ fileToCases :: FilePath -> IO [SchemaTest]
86+ fileToCases name = do
87+ let fullPath = dir </> name
88+ jsonBS <- BS.readFile fullPath
89+ case eitherDecodeStrict jsonBS of
90+ Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e
91+ Right schemaTests -> pure $ prependFileName name <$> schemaTests
92+
93+ prependFileName :: FilePath -> SchemaTest -> SchemaTest
94+ prependFileName fileName s = s
95+ { _stDescription = T.pack fileName <> ": " <> _stDescription s
96+ }
97+
98+toTest
99+ :: forall schema. FromJSON schema
100+ => (schema -> SchemaTestCase -> Expectation)
101+ -> SchemaTest
102+ -> Spec
103+toTest validate st =
104+ it (T.unpack (_stDescription st)) $
105+ forM_ (_stCases st) (validate schema)
106+ where
107+ schema :: schema
108+ schema = case fromJSON (_stSchema st) of
109+ Error e -> panic ("Couldn't parse schema: " <> show e)
110+ Success a -> a
111+
112+assertResult :: Show err => SchemaTestCase -> [err] -> Expectation
113+assertResult sc failures
114+ | _scValid sc = assertValid sc failures
115+ | otherwise = assertInvalid sc failures
116+
117+assertValid :: Show err => SchemaTestCase -> [err] -> Expectation
118+assertValid _ [] = pure ()
119+assertValid sc failures =
120+ expectationFailure $ unlines
121+ [ " Failed to validate data"
122+ , " Description: " <> T.unpack (_scDescription sc)
123+ , " Data: " <> show (_scData sc)
124+ , " Validation failures: " <> show failures
125+ ]
126+
127+assertInvalid :: SchemaTestCase -> [err] -> Expectation
128+assertInvalid sc [] =
129+ expectationFailure $ unlines
130+ [ " Validated invalid data"
131+ , " Description: " <> T.unpack (_scDescription sc)
132+ , " Data: " <> show (_scData sc)
133+ ]
134+assertInvalid _ _ = pure ()
test/supplement/recurse.jsonnew file+17
@@ -0,0 +1,17 @@
1+[
2+ {
3+ "description": "recursive ref",
4+ "schema": {
5+ "allOf": [
6+ {"$ref": "#"}
7+ ]
8+ },
9+ "tests": [
10+ {
11+ "description": "arbitrary data",
12+ "data": {},
13+ "valid": false
14+ }
15+ ]
16+ }
17+]
tests/Lib.hsdeleted−105
@@ -1,105 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−{-# OPTIONS_GHC -fno-warn-orphans #-}
3−
4−module Lib where
5−
6−import Control.Applicative
7−import Control.Monad
8−import Data.Aeson
9−import Data.Aeson.TH
10−import qualified Data.ByteString.Lazy as LBS
11−import Data.Char (toLower)
12−import qualified Data.HashMap.Strict as H
13−import Data.JsonSchema
14−import Data.Monoid
15−import Data.Text (Text)
16−import qualified Data.Text as T
17−import Data.Vector (Vector)
18−import qualified Data.Vector as V
19−import System.FilePath ((</>))
20−import Test.Framework (Test)
21−import Test.Framework.Providers.HUnit (testCase)
22−import Test.HUnit hiding (Test)
23−
24−isLocal :: String -> Bool
25−isLocal file = (file /= "definitions.json")
26− && (file /= "ref.json")
27− && (file /= "refRemote.json")
28−
29−data SchemaTest = SchemaTest
30− { _stDescription :: Text
31− , _stSchema :: RawSchema
32− , _stCases :: [SchemaTestCase]
33− }
34−
35−data SchemaTestCase = SchemaTestCase
36− { _scDescription :: Text
37− , _scData :: Value
38− , _scValid :: Bool
39− }
40−
41−instance FromJSON RawSchema where
42− parseJSON = withObject "Schema" $ \o ->
43− return RawSchema { _rsURI = "", _rsObject = o }
44−
45−instance FromJSON SchemaTest where
46− parseJSON = withObject "SchemaTest" $ \o -> SchemaTest
47− <$> o .: "description"
48− <*> o .: "schema"
49− <*> o .: "tests" -- I wish this were "cases"
50−
51−readSchemaTests :: String -> [String] -> IO [SchemaTest]
52−readSchemaTests dir jsonFiles = concatMapM fileToCases jsonFiles
53− where
54− -- Each file contains an array of SchemaTests, not just one.
55− fileToCases :: String -> IO [SchemaTest]
56− fileToCases name = do
57− let fullPath = dir </> name
58− jsonBS <- LBS.readFile fullPath
59− case eitherDecode jsonBS of
60− Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e
61− Right schemaTests -> return $ prependFileName name <$> schemaTests
62−
63− prependFileName :: String -> SchemaTest -> SchemaTest
64− prependFileName fileName s = s
65− { _stDescription = T.pack fileName <> ": " <> _stDescription s
66− }
67−
68− concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b]
69− concatMapM f xs = liftM concat (mapM f xs)
70−
71−toTest :: SchemaTest -> Test
72−toTest st =
73− testCase (T.unpack $ _stDescription st) $ do
74− assertEqual "schema validity errors" V.empty $ isValidSchema (_stSchema st)
75− forM_ (_stCases st) $ \sc -> do
76− g <- assertRight =<< fetchRefs draft4 (_stSchema st) H.empty
77− let es = validate (compile draft4 g $ _stSchema st) (_scData sc)
78− if _scValid sc
79− then assertValid sc es
80− else assertInvalid sc es
81−
82−assertValid :: SchemaTestCase -> Vector ValErr -> Assertion
83−assertValid sc es =
84− unless (V.length es == 0) $ assertFailure $ unlines
85− [ " Failed to validate data"
86− , " Description: " <> T.unpack (_scDescription sc)
87− , " Data: " <> show (_scData sc)
88− , " Errors: " <> show es
89− ]
90−
91−assertInvalid :: SchemaTestCase -> Vector ValErr -> Assertion
92−assertInvalid sc es =
93− unless (V.length es > 0) $ assertFailure $ unlines
94− [ " Failed to invalidate data"
95− , " Description: " <> T.unpack (_scDescription sc)
96− , " Data: " <> show (_scData sc)
97− ]
98−
99−assertRight :: (Show a) => Either a b -> IO b
100−assertRight a =
101− case a of
102− Left e -> assertFailure (show e) >> fail "assertRight failed"
103− Right b -> return b
104−
105−$(deriveFromJSON defaultOptions { fieldLabelModifier = map toLower . drop 3 } ''SchemaTestCase)
tests/Local.hsdeleted−18
@@ -1,18 +0,0 @@
1−module Main where
2−
3−import Control.Applicative
4−import Data.List (isSuffixOf)
5−import Lib
6−import System.Directory (getDirectoryContents)
7−import Test.Framework
8−
9−main :: IO ()
10−main = do
11− filenames <- filter isLocal . filter (".json" `isSuffixOf`)
12− <$> getDirectoryContents dir
13− ts <- readSchemaTests dir filenames
14− defaultMain (toTest <$> ts)
15−
16− where
17− dir :: String
18− dir = "JSON-Schema-Test-Suite/tests/draft4"
tests/Remote.hsdeleted−23
@@ -1,23 +0,0 @@
1−module Main where
2−
3−import Control.Applicative
4−import Control.Concurrent.Async (withAsync)
5−import Data.List (isSuffixOf)
6−import Lib
7−import Network.Wai.Application.Static (defaultFileServerSettings,
8− staticApp)
9−import Network.Wai.Handler.Warp (run)
10−import System.Directory (getDirectoryContents)
11−import Test.Framework
12−
13−main :: IO ()
14−main =
15− withAsync (run 1234 $ staticApp $ defaultFileServerSettings "JSON-Schema-Test-Suite/remotes") $ \_ -> do
16− filenames <- filter (not . isLocal) . filter (".json" `isSuffixOf`)
17− <$> getDirectoryContents dir
18− ts <- readSchemaTests dir filenames
19− defaultMain (toTest <$> ts)
20−
21− where
22− dir :: String
23− dir = "JSON-Schema-Test-Suite/tests/draft4"