summaryrefslogtreecommitdiff
path: root/json
diff options
context:
space:
mode:
Diffstat (limited to 'json')
-rw-r--r--json/.github/workflows/ci.yml25
-rw-r--r--json/.gitignore1
-rw-r--r--json/LICENSE19
-rw-r--r--json/README.md220
-rwxr-xr-xjson/bin/jsonschema_suite245
-rw-r--r--json/index.js46
-rw-r--r--json/package.json28
-rw-r--r--json/remotes/baseUriChange/folderInteger.json3
-rw-r--r--json/remotes/baseUriChangeFolder/folderInteger.json3
-rw-r--r--json/remotes/baseUriChangeFolderInSubschema/folderInteger.json3
-rw-r--r--json/remotes/draft-next/format-assertion-false.json12
-rw-r--r--json/remotes/draft-next/format-assertion-true.json12
-rw-r--r--json/remotes/draft-next/metaschema-no-validation.json11
-rw-r--r--json/remotes/draft2019-09/metaschema-no-validation.json11
-rw-r--r--json/remotes/draft2020-12/format-assertion-false.json12
-rw-r--r--json/remotes/draft2020-12/format-assertion-true.json12
-rw-r--r--json/remotes/draft2020-12/metaschema-no-validation.json11
-rw-r--r--json/remotes/extendible-dynamic-ref.json20
-rw-r--r--json/remotes/integer.json3
-rw-r--r--json/remotes/name-defs.json15
-rw-r--r--json/remotes/name.json15
-rw-r--r--json/remotes/ref-and-definitions.json11
-rw-r--r--json/remotes/ref-and-defs.json11
-rw-r--r--json/remotes/subSchemas-defs.json10
-rw-r--r--json/remotes/subSchemas.json8
-rw-r--r--json/remotes/tree.json16
-rw-r--r--json/test-schema.json91
-rw-r--r--json/tests/draft-next/additionalProperties.json133
-rw-r--r--json/tests/draft-next/allOf.json294
-rw-r--r--json/tests/draft-next/anchor.json173
-rw-r--r--json/tests/draft-next/anyOf.json189
-rw-r--r--json/tests/draft-next/boolean_schema.json104
-rw-r--r--json/tests/draft-next/const.json342
-rw-r--r--json/tests/draft-next/contains.json241
-rw-r--r--json/tests/draft-next/content.json127
-rw-r--r--json/tests/draft-next/default.json79
-rw-r--r--json/tests/draft-next/defs.json20
-rw-r--r--json/tests/draft-next/dependentRequired.json142
-rw-r--r--json/tests/draft-next/dependentSchemas.json129
-rw-r--r--json/tests/draft-next/dynamicRef.json476
-rw-r--r--json/tests/draft-next/enum.json236
-rw-r--r--json/tests/draft-next/exclusiveMaximum.json30
-rw-r--r--json/tests/draft-next/exclusiveMinimum.json30
-rw-r--r--json/tests/draft-next/format.json686
-rw-r--r--json/tests/draft-next/id.json258
-rw-r--r--json/tests/draft-next/if-then-else.json258
-rw-r--r--json/tests/draft-next/infinite-loop-detection.json36
-rw-r--r--json/tests/draft-next/items.json256
-rw-r--r--json/tests/draft-next/maxContains.json129
-rw-r--r--json/tests/draft-next/maxItems.json28
-rw-r--r--json/tests/draft-next/maxLength.json33
-rw-r--r--json/tests/draft-next/maxProperties.json54
-rw-r--r--json/tests/draft-next/maximum.json54
-rw-r--r--json/tests/draft-next/minContains.json197
-rw-r--r--json/tests/draft-next/minItems.json28
-rw-r--r--json/tests/draft-next/minLength.json33
-rw-r--r--json/tests/draft-next/minProperties.json38
-rw-r--r--json/tests/draft-next/minimum.json69
-rw-r--r--json/tests/draft-next/multipleOf.json71
-rw-r--r--json/tests/draft-next/not.json117
-rw-r--r--json/tests/draft-next/oneOf.json274
-rw-r--r--json/tests/draft-next/optional/bignum.json93
-rw-r--r--json/tests/draft-next/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft-next/optional/float-overflow.json13
-rw-r--r--json/tests/draft-next/optional/format-assertion.json42
-rw-r--r--json/tests/draft-next/optional/format/date-time.json133
-rw-r--r--json/tests/draft-next/optional/format/date.json223
-rw-r--r--json/tests/draft-next/optional/format/duration.json128
-rw-r--r--json/tests/draft-next/optional/format/email.json118
-rw-r--r--json/tests/draft-next/optional/format/hostname.json98
-rw-r--r--json/tests/draft-next/optional/format/idn-email.json58
-rw-r--r--json/tests/draft-next/optional/format/idn-hostname.json304
-rw-r--r--json/tests/draft-next/optional/format/ipv4.json84
-rw-r--r--json/tests/draft-next/optional/format/ipv6.json208
-rw-r--r--json/tests/draft-next/optional/format/iri-reference.json73
-rw-r--r--json/tests/draft-next/optional/format/iri.json83
-rw-r--r--json/tests/draft-next/optional/format/json-pointer.json198
-rw-r--r--json/tests/draft-next/optional/format/regex.json48
-rw-r--r--json/tests/draft-next/optional/format/relative-json-pointer.json83
-rw-r--r--json/tests/draft-next/optional/format/time.json198
-rw-r--r--json/tests/draft-next/optional/format/uri-reference.json73
-rw-r--r--json/tests/draft-next/optional/format/uri-template.json58
-rw-r--r--json/tests/draft-next/optional/format/uri.json108
-rw-r--r--json/tests/draft-next/optional/format/uuid.json85
-rw-r--r--json/tests/draft-next/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft-next/optional/refOfUnknownKeyword.json44
-rw-r--r--json/tests/draft-next/pattern.json59
-rw-r--r--json/tests/draft-next/patternProperties.json156
-rw-r--r--json/tests/draft-next/prefixItems.json81
-rw-r--r--json/tests/draft-next/properties.json167
-rw-r--r--json/tests/draft-next/propertyNames.json78
-rw-r--r--json/tests/draft-next/ref.json581
-rw-r--r--json/tests/draft-next/refRemote.json190
-rw-r--r--json/tests/draft-next/required.json105
-rw-r--r--json/tests/draft-next/type.json474
-rw-r--r--json/tests/draft-next/unevaluatedItems.json633
-rw-r--r--json/tests/draft-next/unevaluatedProperties.json1396
-rw-r--r--json/tests/draft-next/uniqueItems.json404
-rw-r--r--json/tests/draft-next/unknownKeyword.json56
-rw-r--r--json/tests/draft-next/vocabulary.json38
-rw-r--r--json/tests/draft2019-09/additionalItems.json149
-rw-r--r--json/tests/draft2019-09/additionalProperties.json133
-rw-r--r--json/tests/draft2019-09/allOf.json294
-rw-r--r--json/tests/draft2019-09/anchor.json173
-rw-r--r--json/tests/draft2019-09/anyOf.json189
-rw-r--r--json/tests/draft2019-09/boolean_schema.json104
-rw-r--r--json/tests/draft2019-09/const.json342
-rw-r--r--json/tests/draft2019-09/contains.json150
-rw-r--r--json/tests/draft2019-09/content.json127
-rw-r--r--json/tests/draft2019-09/default.json79
-rw-r--r--json/tests/draft2019-09/defs.json18
-rw-r--r--json/tests/draft2019-09/dependentRequired.json142
-rw-r--r--json/tests/draft2019-09/dependentSchemas.json129
-rw-r--r--json/tests/draft2019-09/enum.json236
-rw-r--r--json/tests/draft2019-09/exclusiveMaximum.json30
-rw-r--r--json/tests/draft2019-09/exclusiveMinimum.json30
-rw-r--r--json/tests/draft2019-09/format.json686
-rw-r--r--json/tests/draft2019-09/id.json256
-rw-r--r--json/tests/draft2019-09/if-then-else.json258
-rw-r--r--json/tests/draft2019-09/infinite-loop-detection.json36
-rw-r--r--json/tests/draft2019-09/items.json250
-rw-r--r--json/tests/draft2019-09/maxContains.json79
-rw-r--r--json/tests/draft2019-09/maxItems.json28
-rw-r--r--json/tests/draft2019-09/maxLength.json33
-rw-r--r--json/tests/draft2019-09/maxProperties.json54
-rw-r--r--json/tests/draft2019-09/maximum.json54
-rw-r--r--json/tests/draft2019-09/minContains.json197
-rw-r--r--json/tests/draft2019-09/minItems.json28
-rw-r--r--json/tests/draft2019-09/minLength.json33
-rw-r--r--json/tests/draft2019-09/minProperties.json38
-rw-r--r--json/tests/draft2019-09/minimum.json69
-rw-r--r--json/tests/draft2019-09/multipleOf.json71
-rw-r--r--json/tests/draft2019-09/not.json117
-rw-r--r--json/tests/draft2019-09/oneOf.json274
-rw-r--r--json/tests/draft2019-09/optional/bignum.json93
-rw-r--r--json/tests/draft2019-09/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft2019-09/optional/float-overflow.json13
-rw-r--r--json/tests/draft2019-09/optional/format/date-time.json133
-rw-r--r--json/tests/draft2019-09/optional/format/date.json223
-rw-r--r--json/tests/draft2019-09/optional/format/duration.json128
-rw-r--r--json/tests/draft2019-09/optional/format/email.json83
-rw-r--r--json/tests/draft2019-09/optional/format/hostname.json98
-rw-r--r--json/tests/draft2019-09/optional/format/idn-email.json58
-rw-r--r--json/tests/draft2019-09/optional/format/idn-hostname.json304
-rw-r--r--json/tests/draft2019-09/optional/format/ipv4.json84
-rw-r--r--json/tests/draft2019-09/optional/format/ipv6.json208
-rw-r--r--json/tests/draft2019-09/optional/format/iri-reference.json73
-rw-r--r--json/tests/draft2019-09/optional/format/iri.json83
-rw-r--r--json/tests/draft2019-09/optional/format/json-pointer.json198
-rw-r--r--json/tests/draft2019-09/optional/format/regex.json48
-rw-r--r--json/tests/draft2019-09/optional/format/relative-json-pointer.json83
-rw-r--r--json/tests/draft2019-09/optional/format/time.json198
-rw-r--r--json/tests/draft2019-09/optional/format/unknown.json43
-rw-r--r--json/tests/draft2019-09/optional/format/uri-reference.json73
-rw-r--r--json/tests/draft2019-09/optional/format/uri-template.json58
-rw-r--r--json/tests/draft2019-09/optional/format/uri.json108
-rw-r--r--json/tests/draft2019-09/optional/format/uuid.json85
-rw-r--r--json/tests/draft2019-09/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft2019-09/optional/refOfUnknownKeyword.json44
-rw-r--r--json/tests/draft2019-09/pattern.json59
-rw-r--r--json/tests/draft2019-09/patternProperties.json156
-rw-r--r--json/tests/draft2019-09/properties.json167
-rw-r--r--json/tests/draft2019-09/propertyNames.json107
-rw-r--r--json/tests/draft2019-09/recursiveRef.json399
-rw-r--r--json/tests/draft2019-09/ref.json579
-rw-r--r--json/tests/draft2019-09/refRemote.json190
-rw-r--r--json/tests/draft2019-09/required.json105
-rw-r--r--json/tests/draft2019-09/type.json474
-rw-r--r--json/tests/draft2019-09/unevaluatedItems.json525
-rw-r--r--json/tests/draft2019-09/unevaluatedProperties.json1347
-rw-r--r--json/tests/draft2019-09/uniqueItems.json404
-rw-r--r--json/tests/draft2019-09/unknownKeyword.json56
-rw-r--r--json/tests/draft2019-09/vocabulary.json38
-rw-r--r--json/tests/draft2020-12/additionalProperties.json133
-rw-r--r--json/tests/draft2020-12/allOf.json294
-rw-r--r--json/tests/draft2020-12/anchor.json173
-rw-r--r--json/tests/draft2020-12/anyOf.json189
-rw-r--r--json/tests/draft2020-12/boolean_schema.json104
-rw-r--r--json/tests/draft2020-12/const.json342
-rw-r--r--json/tests/draft2020-12/contains.json150
-rw-r--r--json/tests/draft2020-12/content.json127
-rw-r--r--json/tests/draft2020-12/default.json79
-rw-r--r--json/tests/draft2020-12/defs.json20
-rw-r--r--json/tests/draft2020-12/dependentRequired.json142
-rw-r--r--json/tests/draft2020-12/dependentSchemas.json129
-rw-r--r--json/tests/draft2020-12/dynamicRef.json619
-rw-r--r--json/tests/draft2020-12/enum.json236
-rw-r--r--json/tests/draft2020-12/exclusiveMaximum.json30
-rw-r--r--json/tests/draft2020-12/exclusiveMinimum.json30
-rw-r--r--json/tests/draft2020-12/format.json686
-rw-r--r--json/tests/draft2020-12/id.json258
-rw-r--r--json/tests/draft2020-12/if-then-else.json258
-rw-r--r--json/tests/draft2020-12/infinite-loop-detection.json36
-rw-r--r--json/tests/draft2020-12/items.json256
-rw-r--r--json/tests/draft2020-12/maxContains.json79
-rw-r--r--json/tests/draft2020-12/maxItems.json28
-rw-r--r--json/tests/draft2020-12/maxLength.json33
-rw-r--r--json/tests/draft2020-12/maxProperties.json54
-rw-r--r--json/tests/draft2020-12/maximum.json54
-rw-r--r--json/tests/draft2020-12/minContains.json197
-rw-r--r--json/tests/draft2020-12/minItems.json28
-rw-r--r--json/tests/draft2020-12/minLength.json33
-rw-r--r--json/tests/draft2020-12/minProperties.json38
-rw-r--r--json/tests/draft2020-12/minimum.json69
-rw-r--r--json/tests/draft2020-12/multipleOf.json71
-rw-r--r--json/tests/draft2020-12/not.json117
-rw-r--r--json/tests/draft2020-12/oneOf.json274
-rw-r--r--json/tests/draft2020-12/optional/bignum.json93
-rw-r--r--json/tests/draft2020-12/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft2020-12/optional/float-overflow.json13
-rw-r--r--json/tests/draft2020-12/optional/format-assertion.json42
-rw-r--r--json/tests/draft2020-12/optional/format/date-time.json133
-rw-r--r--json/tests/draft2020-12/optional/format/date.json223
-rw-r--r--json/tests/draft2020-12/optional/format/duration.json128
-rw-r--r--json/tests/draft2020-12/optional/format/email.json118
-rw-r--r--json/tests/draft2020-12/optional/format/hostname.json98
-rw-r--r--json/tests/draft2020-12/optional/format/idn-email.json58
-rw-r--r--json/tests/draft2020-12/optional/format/idn-hostname.json304
-rw-r--r--json/tests/draft2020-12/optional/format/ipv4.json84
-rw-r--r--json/tests/draft2020-12/optional/format/ipv6.json208
-rw-r--r--json/tests/draft2020-12/optional/format/iri-reference.json73
-rw-r--r--json/tests/draft2020-12/optional/format/iri.json83
-rw-r--r--json/tests/draft2020-12/optional/format/json-pointer.json198
-rw-r--r--json/tests/draft2020-12/optional/format/regex.json48
-rw-r--r--json/tests/draft2020-12/optional/format/relative-json-pointer.json83
-rw-r--r--json/tests/draft2020-12/optional/format/time.json198
-rw-r--r--json/tests/draft2020-12/optional/format/unknown.json43
-rw-r--r--json/tests/draft2020-12/optional/format/uri-reference.json73
-rw-r--r--json/tests/draft2020-12/optional/format/uri-template.json58
-rw-r--r--json/tests/draft2020-12/optional/format/uri.json108
-rw-r--r--json/tests/draft2020-12/optional/format/uuid.json85
-rw-r--r--json/tests/draft2020-12/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft2020-12/optional/refOfUnknownKeyword.json44
-rw-r--r--json/tests/draft2020-12/pattern.json59
-rw-r--r--json/tests/draft2020-12/patternProperties.json156
-rw-r--r--json/tests/draft2020-12/prefixItems.json81
-rw-r--r--json/tests/draft2020-12/properties.json167
-rw-r--r--json/tests/draft2020-12/propertyNames.json78
-rw-r--r--json/tests/draft2020-12/ref.json581
-rw-r--r--json/tests/draft2020-12/refRemote.json190
-rw-r--r--json/tests/draft2020-12/required.json105
-rw-r--r--json/tests/draft2020-12/type.json474
-rw-r--r--json/tests/draft2020-12/unevaluatedItems.json633
-rw-r--r--json/tests/draft2020-12/unevaluatedProperties.json1347
-rw-r--r--json/tests/draft2020-12/uniqueItems.json404
-rw-r--r--json/tests/draft2020-12/unknownKeyword.json56
-rw-r--r--json/tests/draft2020-12/vocabulary.json38
-rw-r--r--json/tests/draft3/additionalItems.json113
-rw-r--r--json/tests/draft3/additionalProperties.json133
-rw-r--r--json/tests/draft3/default.json79
-rw-r--r--json/tests/draft3/dependencies.json123
-rw-r--r--json/tests/draft3/disallow.json80
-rw-r--r--json/tests/draft3/divisibleBy.json60
-rw-r--r--json/tests/draft3/enum.json118
-rw-r--r--json/tests/draft3/extends.json94
-rw-r--r--json/tests/draft3/format.json362
-rw-r--r--json/tests/draft3/infinite-loop-detection.json32
-rw-r--r--json/tests/draft3/items.json46
-rw-r--r--json/tests/draft3/maxItems.json28
-rw-r--r--json/tests/draft3/maxLength.json33
-rw-r--r--json/tests/draft3/maximum.json99
-rw-r--r--json/tests/draft3/minItems.json28
-rw-r--r--json/tests/draft3/minLength.json33
-rw-r--r--json/tests/draft3/minimum.json88
-rw-r--r--json/tests/draft3/optional/bignum.json107
-rw-r--r--json/tests/draft3/optional/ecmascript-regex.json18
-rw-r--r--json/tests/draft3/optional/format/color.json38
-rw-r--r--json/tests/draft3/optional/format/date-time.json38
-rw-r--r--json/tests/draft3/optional/format/date.json168
-rw-r--r--json/tests/draft3/optional/format/email.json53
-rw-r--r--json/tests/draft3/optional/format/host-name.json63
-rw-r--r--json/tests/draft3/optional/format/ip-address.json23
-rw-r--r--json/tests/draft3/optional/format/ipv6.json68
-rw-r--r--json/tests/draft3/optional/format/regex.json18
-rw-r--r--json/tests/draft3/optional/format/time.json18
-rw-r--r--json/tests/draft3/optional/format/uri.json28
-rw-r--r--json/tests/draft3/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft3/optional/zeroTerminatedFloats.json15
-rw-r--r--json/tests/draft3/pattern.json59
-rw-r--r--json/tests/draft3/patternProperties.json115
-rw-r--r--json/tests/draft3/properties.json97
-rw-r--r--json/tests/draft3/ref.json278
-rw-r--r--json/tests/draft3/refRemote.json74
-rw-r--r--json/tests/draft3/required.json53
-rw-r--r--json/tests/draft3/type.json494
-rw-r--r--json/tests/draft3/uniqueItems.json374
-rw-r--r--json/tests/draft4/additionalItems.json149
-rw-r--r--json/tests/draft4/additionalProperties.json133
-rw-r--r--json/tests/draft4/allOf.json261
-rw-r--r--json/tests/draft4/anyOf.json182
-rw-r--r--json/tests/draft4/default.json79
-rw-r--r--json/tests/draft4/definitions.json26
-rw-r--r--json/tests/draft4/dependencies.json194
-rw-r--r--json/tests/draft4/enum.json236
-rw-r--r--json/tests/draft4/format.json218
-rw-r--r--json/tests/draft4/id.json53
-rw-r--r--json/tests/draft4/infinite-loop-detection.json36
-rw-r--r--json/tests/draft4/items.json195
-rw-r--r--json/tests/draft4/maxItems.json28
-rw-r--r--json/tests/draft4/maxLength.json33
-rw-r--r--json/tests/draft4/maxProperties.json54
-rw-r--r--json/tests/draft4/maximum.json99
-rw-r--r--json/tests/draft4/minItems.json28
-rw-r--r--json/tests/draft4/minLength.json33
-rw-r--r--json/tests/draft4/minProperties.json38
-rw-r--r--json/tests/draft4/minimum.json114
-rw-r--r--json/tests/draft4/multipleOf.json71
-rw-r--r--json/tests/draft4/not.json96
-rw-r--r--json/tests/draft4/oneOf.json230
-rw-r--r--json/tests/draft4/optional/bignum.json95
-rw-r--r--json/tests/draft4/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft4/optional/float-overflow.json13
-rw-r--r--json/tests/draft4/optional/format/date-time.json133
-rw-r--r--json/tests/draft4/optional/format/email.json83
-rw-r--r--json/tests/draft4/optional/format/hostname.json98
-rw-r--r--json/tests/draft4/optional/format/ipv4.json84
-rw-r--r--json/tests/draft4/optional/format/ipv6.json208
-rw-r--r--json/tests/draft4/optional/format/unknown.json43
-rw-r--r--json/tests/draft4/optional/format/uri.json108
-rw-r--r--json/tests/draft4/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft4/optional/zeroTerminatedFloats.json15
-rw-r--r--json/tests/draft4/pattern.json59
-rw-r--r--json/tests/draft4/patternProperties.json120
-rw-r--r--json/tests/draft4/properties.json136
-rw-r--r--json/tests/draft4/ref.json507
-rw-r--r--json/tests/draft4/refRemote.json171
-rw-r--r--json/tests/draft4/required.json89
-rw-r--r--json/tests/draft4/type.json469
-rw-r--r--json/tests/draft4/uniqueItems.json404
-rw-r--r--json/tests/draft6/additionalItems.json149
-rw-r--r--json/tests/draft6/additionalProperties.json133
-rw-r--r--json/tests/draft6/allOf.json294
-rw-r--r--json/tests/draft6/anyOf.json215
-rw-r--r--json/tests/draft6/boolean_schema.json104
-rw-r--r--json/tests/draft6/const.json342
-rw-r--r--json/tests/draft6/contains.json129
-rw-r--r--json/tests/draft6/default.json79
-rw-r--r--json/tests/draft6/definitions.json26
-rw-r--r--json/tests/draft6/dependencies.json248
-rw-r--r--json/tests/draft6/enum.json236
-rw-r--r--json/tests/draft6/exclusiveMaximum.json30
-rw-r--r--json/tests/draft6/exclusiveMinimum.json30
-rw-r--r--json/tests/draft6/format.json326
-rw-r--r--json/tests/draft6/id.json53
-rw-r--r--json/tests/draft6/infinite-loop-detection.json36
-rw-r--r--json/tests/draft6/items.json250
-rw-r--r--json/tests/draft6/maxItems.json28
-rw-r--r--json/tests/draft6/maxLength.json33
-rw-r--r--json/tests/draft6/maxProperties.json54
-rw-r--r--json/tests/draft6/maximum.json54
-rw-r--r--json/tests/draft6/minItems.json28
-rw-r--r--json/tests/draft6/minLength.json33
-rw-r--r--json/tests/draft6/minProperties.json38
-rw-r--r--json/tests/draft6/minimum.json69
-rw-r--r--json/tests/draft6/multipleOf.json71
-rw-r--r--json/tests/draft6/not.json117
-rw-r--r--json/tests/draft6/oneOf.json274
-rw-r--r--json/tests/draft6/optional/bignum.json93
-rw-r--r--json/tests/draft6/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft6/optional/float-overflow.json13
-rw-r--r--json/tests/draft6/optional/format/date-time.json133
-rw-r--r--json/tests/draft6/optional/format/email.json83
-rw-r--r--json/tests/draft6/optional/format/hostname.json98
-rw-r--r--json/tests/draft6/optional/format/ipv4.json84
-rw-r--r--json/tests/draft6/optional/format/ipv6.json208
-rw-r--r--json/tests/draft6/optional/format/json-pointer.json198
-rw-r--r--json/tests/draft6/optional/format/unknown.json43
-rw-r--r--json/tests/draft6/optional/format/uri-reference.json73
-rw-r--r--json/tests/draft6/optional/format/uri-template.json58
-rw-r--r--json/tests/draft6/optional/format/uri.json108
-rw-r--r--json/tests/draft6/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft6/pattern.json59
-rw-r--r--json/tests/draft6/patternProperties.json156
-rw-r--r--json/tests/draft6/properties.json167
-rw-r--r--json/tests/draft6/propertyNames.json107
-rw-r--r--json/tests/draft6/ref.json612
-rw-r--r--json/tests/draft6/refRemote.json196
-rw-r--r--json/tests/draft6/required.json105
-rw-r--r--json/tests/draft6/type.json474
-rw-r--r--json/tests/draft6/uniqueItems.json404
-rw-r--r--json/tests/draft6/unknownKeyword.json56
-rw-r--r--json/tests/draft7/additionalItems.json149
-rw-r--r--json/tests/draft7/additionalProperties.json133
-rw-r--r--json/tests/draft7/allOf.json294
-rw-r--r--json/tests/draft7/anyOf.json215
-rw-r--r--json/tests/draft7/boolean_schema.json104
-rw-r--r--json/tests/draft7/const.json342
-rw-r--r--json/tests/draft7/contains.json150
-rw-r--r--json/tests/draft7/default.json79
-rw-r--r--json/tests/draft7/definitions.json26
-rw-r--r--json/tests/draft7/dependencies.json248
-rw-r--r--json/tests/draft7/enum.json236
-rw-r--r--json/tests/draft7/exclusiveMaximum.json30
-rw-r--r--json/tests/draft7/exclusiveMinimum.json30
-rw-r--r--json/tests/draft7/format.json614
-rw-r--r--json/tests/draft7/id.json53
-rw-r--r--json/tests/draft7/if-then-else.json258
-rw-r--r--json/tests/draft7/infinite-loop-detection.json36
-rw-r--r--json/tests/draft7/items.json250
-rw-r--r--json/tests/draft7/maxItems.json28
-rw-r--r--json/tests/draft7/maxLength.json33
-rw-r--r--json/tests/draft7/maxProperties.json54
-rw-r--r--json/tests/draft7/maximum.json54
-rw-r--r--json/tests/draft7/minItems.json28
-rw-r--r--json/tests/draft7/minLength.json33
-rw-r--r--json/tests/draft7/minProperties.json38
-rw-r--r--json/tests/draft7/minimum.json69
-rw-r--r--json/tests/draft7/multipleOf.json71
-rw-r--r--json/tests/draft7/not.json117
-rw-r--r--json/tests/draft7/oneOf.json274
-rw-r--r--json/tests/draft7/optional/bignum.json93
-rw-r--r--json/tests/draft7/optional/content.json77
-rw-r--r--json/tests/draft7/optional/ecmascript-regex.json552
-rw-r--r--json/tests/draft7/optional/float-overflow.json13
-rw-r--r--json/tests/draft7/optional/format/date-time.json133
-rw-r--r--json/tests/draft7/optional/format/date.json223
-rw-r--r--json/tests/draft7/optional/format/email.json83
-rw-r--r--json/tests/draft7/optional/format/hostname.json98
-rw-r--r--json/tests/draft7/optional/format/idn-email.json58
-rw-r--r--json/tests/draft7/optional/format/idn-hostname.json304
-rw-r--r--json/tests/draft7/optional/format/ipv4.json84
-rw-r--r--json/tests/draft7/optional/format/ipv6.json208
-rw-r--r--json/tests/draft7/optional/format/iri-reference.json73
-rw-r--r--json/tests/draft7/optional/format/iri.json83
-rw-r--r--json/tests/draft7/optional/format/json-pointer.json198
-rw-r--r--json/tests/draft7/optional/format/regex.json48
-rw-r--r--json/tests/draft7/optional/format/relative-json-pointer.json83
-rw-r--r--json/tests/draft7/optional/format/time.json198
-rw-r--r--json/tests/draft7/optional/format/unknown.json43
-rw-r--r--json/tests/draft7/optional/format/uri-reference.json73
-rw-r--r--json/tests/draft7/optional/format/uri-template.json58
-rw-r--r--json/tests/draft7/optional/format/uri.json108
-rw-r--r--json/tests/draft7/optional/non-bmp-regex.json82
-rw-r--r--json/tests/draft7/pattern.json59
-rw-r--r--json/tests/draft7/patternProperties.json156
-rw-r--r--json/tests/draft7/properties.json167
-rw-r--r--json/tests/draft7/propertyNames.json107
-rw-r--r--json/tests/draft7/ref.json648
-rw-r--r--json/tests/draft7/refRemote.json196
-rw-r--r--json/tests/draft7/required.json105
-rw-r--r--json/tests/draft7/type.json474
-rw-r--r--json/tests/draft7/uniqueItems.json404
-rw-r--r--json/tests/draft7/unknownKeyword.json56
l---------json/tests/latest1
-rw-r--r--json/tox.ini9
445 files changed, 66380 insertions, 0 deletions
diff --git a/json/.github/workflows/ci.yml b/json/.github/workflows/ci.yml
new file mode 100644
index 0000000..9fdb508
--- /dev/null
+++ b/json/.github/workflows/ci.yml
@@ -0,0 +1,25 @@
+name: Test Suite Sanity Checking
+
+on:
+ push:
+ pull_request:
+ release:
+ types: [published]
+ schedule:
+ # Daily at 6:42
+ - cron: '42 6 * * *'
+
+jobs:
+ ci:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.7
+ - name: Install tox
+ run: python -m pip install tox
+ - name: Run the sanity checks
+ run: python -m tox
diff --git a/json/.gitignore b/json/.gitignore
new file mode 100644
index 0000000..1333ed7
--- /dev/null
+++ b/json/.gitignore
@@ -0,0 +1 @@
+TODO
diff --git a/json/LICENSE b/json/LICENSE
new file mode 100644
index 0000000..c28adba
--- /dev/null
+++ b/json/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Julian Berman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/json/README.md b/json/README.md
new file mode 100644
index 0000000..b5cd6d5
--- /dev/null
+++ b/json/README.md
@@ -0,0 +1,220 @@
+# JSON Schema Test Suite
+[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/json-schema-org/.github/blob/main/CODE_OF_CONDUCT.md)
+[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
+[![Financial Contributors on Open Collective](https://opencollective.com/json-schema/all/badge.svg?label=financial+contributors)](https://opencollective.com/json-schema)
+
+[![Build Status](https://github.com/json-schema-org/JSON-Schema-Test-Suite/workflows/Test%20Suite%20Sanity%20Checking/badge.svg)](https://github.com/json-schema-org/JSON-Schema-Test-Suite/actions?query=workflow%3A%22Test+Suite+Sanity+Checking%22)
+
+This repository contains a set of JSON objects that implementors of JSON Schema
+validation libraries can use to test their validators.
+
+It is meant to be language agnostic and should require only a JSON parser.
+
+The conversion of the JSON objects into tests within your test framework of
+choice is still the job of the validator implementor.
+
+## Structure of a Test
+
+The tests in this suite are contained in the `tests` directory at the root of
+this repository. Inside that directory is a subdirectory for each draft or
+version of the specification.
+
+Inside each draft directory, there are a number of `.json` files and one or more
+special subdirectories. The subdirectories contain `.json` files meant for a
+specific testing purpose, and each `.json` file logically groups a set of test
+cases together. Often the grouping is by property under test, but not always.
+
+The subdirectories are described in the next section.
+
+Inside each `.json` file is a single array containing objects. It's easiest to
+illustrate the structure of these with an example:
+
+```json
+{
+ "description": "The description of the test case",
+ "schema": {
+ "description": "The schema against which the data in each test is validated",
+ "type": "string"
+ },
+ "tests": [
+ {
+ "description": "Test for a valid instance",
+ "data": "the instance to validate",
+ "valid": true
+ },
+ {
+ "description": "Test for an invalid instance",
+ "data": 15,
+ "valid": false
+ }
+ ]
+}
+```
+
+In short: a description, a schema under test, and some tests, where each test
+in the `tests` array is an objects with a description of the case itself, the
+instance under test, and a boolean indicating whether it should be valid
+or invalid.
+
+## Test Subdirectories
+
+There is currently only one subdirectory that may exist within each draft
+directory. This is:
+
+1. `optional/`: Contains tests that are considered optional.
+
+## Coverage
+
+All JSON Schema specification releases should be well covered by this suite,
+including drafts 2020-12, 2019-09, 07, 06, 04 and 03. Additional coverage is
+always welcome, particularly for bugs encountered in real-world
+implementations.
+
+Drafts 04 and 03 are considered "frozen" in that less effort is put in to
+backport new tests to these versions.
+
+Contributions are very welcome, especially from implementers as they add support
+to their own implementations.
+
+If you see anything missing from the current supported drafts, or incorrect on
+any draft still accepting bug fixes, please
+[file an issue](https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues)
+or [submit a PR](https://github.com/json-schema-org/JSON-Schema-Test-Suite).
+
+## Who Uses the Test Suite
+
+This suite is being used by:
+
+### Clojure
+
+* [jinx](https://github.com/juxt/jinx)
+* [json-schema](https://github.com/tatut/json-schema)
+
+### Coffeescript
+
+* [jsck](https://github.com/pandastrike/jsck)
+
+### Common Lisp
+
+* [json-schema](https://github.com/fisxoj/json-schema)
+
+### C++
+
+* [Modern C++ JSON schema validator](https://github.com/pboettch/json-schema-validator)
+
+### Dart
+
+* [json\_schema](https://github.com/patefacio/json_schema)
+
+### Elixir
+
+* [ex\_json\_schema](https://github.com/jonasschmidt/ex_json_schema)
+
+### Erlang
+
+* [jesse](https://github.com/for-GET/jesse)
+
+### Go
+
+* [gojsonschema](https://github.com/sigu-399/gojsonschema)
+* [validate-json](https://github.com/cesanta/validate-json)
+
+### Haskell
+
+* [aeson-schema](https://github.com/timjb/aeson-schema)
+* [hjsonschema](https://github.com/seagreen/hjsonschema)
+
+### Java
+
+* [json-schema-validator](https://github.com/daveclayton/json-schema-validator)
+* [everit-org/json-schema](https://github.com/everit-org/json-schema)
+* [networknt/json-schema-validator](https://github.com/networknt/json-schema-validator)
+* [Justify](https://github.com/leadpony/justify)
+* [Snow](https://github.com/ssilverman/snowy-json)
+* [jsonschemafriend](https://github.com/jimblackler/jsonschemafriend)
+
+### JavaScript
+
+* [json-schema-benchmark](https://github.com/Muscula/json-schema-benchmark)
+* [direct-schema](https://github.com/IreneKnapp/direct-schema)
+* [is-my-json-valid](https://github.com/mafintosh/is-my-json-valid)
+* [jassi](https://github.com/iclanzan/jassi)
+* [JaySchema](https://github.com/natesilva/jayschema)
+* [json-schema-valid](https://github.com/ericgj/json-schema-valid)
+* [Jsonary](https://github.com/jsonary-js/jsonary)
+* [jsonschema](https://github.com/tdegrunt/jsonschema)
+* [request-validator](https://github.com/bugventure/request-validator)
+* [skeemas](https://github.com/Prestaul/skeemas)
+* [tv4](https://github.com/geraintluff/tv4)
+* [z-schema](https://github.com/zaggino/z-schema)
+* [jsen](https://github.com/bugventure/jsen)
+* [ajv](https://github.com/epoberezkin/ajv)
+* [djv](https://github.com/korzio/djv)
+
+### Node.js
+
+For node.js developers, the suite is also available as an
+[npm](https://www.npmjs.com/package/@json-schema-org/tests) package.
+
+Node-specific support is maintained in a [separate
+repository](https://github.com/json-schema-org/json-schema-test-suite-npm)
+which also welcomes your contributions!
+
+### .NET
+
+* [Newtonsoft.Json.Schema](https://github.com/JamesNK/Newtonsoft.Json.Schema)
+* [Manatee.Json](https://github.com/gregsdennis/Manatee.Json)
+
+### Perl
+
+* [JSON::Schema::Draft201909](https://github.com/karenetheridge/JSON-Schema-Draft201909)
+* [JSON::Schema::Tiny](https://github.com/karenetheridge/JSON-Schema-Tiny)
+* [Test::JSON::Schema::Acceptance](https://github.com/karenetheridge/Test-JSON-Schema-Acceptance)
+
+### PHP
+
+* [opis/json-schema](https://github.com/opis/json-schema)
+* [json-schema](https://github.com/justinrainbow/json-schema)
+* [json-guard](https://github.com/thephpleague/json-guard)
+
+### PostgreSQL
+
+* [postgres-json-schema](https://github.com/gavinwahl/postgres-json-schema)
+* [is\_jsonb\_valid](https://github.com/furstenheim/is_jsonb_valid)
+
+### Python
+
+* [jsonschema](https://github.com/Julian/jsonschema)
+* [fastjsonschema](https://github.com/seznam/python-fastjsonschema)
+* [hypothesis-jsonschema](https://github.com/Zac-HD/hypothesis-jsonschema)
+* [jschon](https://github.com/marksparkza/jschon)
+
+### Ruby
+
+* [json-schema](https://github.com/hoxworth/json-schema)
+* [json\_schemer](https://github.com/davishmcclurg/json_schemer)
+
+### Rust
+
+* [jsonschema](https://github.com/Stranger6667/jsonschema-rs)
+* [valico](https://github.com/rustless/valico)
+
+### Scala
+
+* [typed-json](https://github.com/frawa/typed-json)
+
+### Swift
+
+* [JSONSchema](https://github.com/kylef/JSONSchema.swift)
+
+If you use it as well, please fork and send a pull request adding yourself to
+the list :).
+
+## Contributing
+
+If you see something missing or incorrect, a pull request is most welcome!
+
+There are some sanity checks in place for testing the test suite. You can run
+them with `bin/jsonschema_suite check` or `tox`. They will be run automatically
+by [GitHub Actions](https://github.com/json-schema-org/JSON-Schema-Test-Suite/actions?query=workflow%3A%22Test+Suite+Sanity+Checking%22)
+as well.
diff --git a/json/bin/jsonschema_suite b/json/bin/jsonschema_suite
new file mode 100755
index 0000000..102caec
--- /dev/null
+++ b/json/bin/jsonschema_suite
@@ -0,0 +1,245 @@
+#! /usr/bin/env python3
+import argparse
+import errno
+import fnmatch
+import json
+import os
+import random
+import shutil
+import sys
+import textwrap
+import unittest
+import warnings
+
+try:
+ import jsonschema.validators
+except ImportError:
+ jsonschema = None
+
+
+ROOT_DIR = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), os.pardir).rstrip("__pycache__"),
+)
+SUITE_ROOT_DIR = os.path.join(ROOT_DIR, "tests")
+REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
+
+with open(os.path.join(ROOT_DIR, "test-schema.json")) as schema:
+ TESTSUITE_SCHEMA = json.load(schema)
+
+
+def files(paths):
+ """
+ Each test file in the provided paths.
+ """
+ for path in paths:
+ with open(path) as test_file:
+ yield json.load(test_file)
+
+
+def groups(paths):
+ """
+ Each test group within each file in the provided paths.
+ """
+ for test_file in files(paths):
+ for group in test_file:
+ yield group
+
+
+def cases(paths):
+ """
+ Each individual test case within all groups within the provided paths.
+ """
+ for test_group in groups(paths):
+ for test in test_group["tests"]:
+ test["schema"] = test_group["schema"]
+ yield test
+
+
+def collect(root_dir):
+ """
+ All of the test file paths within the given root directory, recursively.
+ """
+ for root, _, files in os.walk(root_dir):
+ for filename in fnmatch.filter(files, "*.json"):
+ yield os.path.join(root, filename)
+
+
+class SanityTests(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ print("Looking for tests in %s" % SUITE_ROOT_DIR)
+ print("Looking for remotes in %s" % REMOTES_DIR)
+ cls.test_files = list(collect(SUITE_ROOT_DIR))
+ cls.remote_files = list(collect(REMOTES_DIR))
+ print("Found %s test files" % len(cls.test_files))
+ print("Found %s remote files" % len(cls.remote_files))
+ assert cls.test_files, "Didn't find the test files!"
+ assert cls.remote_files, "Didn't find the remote files!"
+
+ def test_all_test_files_are_valid_json(self):
+ for path in self.test_files:
+ with open(path) as test_file:
+ try:
+ json.load(test_file)
+ except ValueError as error:
+ self.fail("%s contains invalid JSON (%s)" % (path, error))
+
+ def test_all_remote_files_are_valid_json(self):
+ for path in self.remote_files:
+ with open(path) as remote_file:
+ try:
+ json.load(remote_file)
+ except ValueError as error:
+ self.fail("%s contains invalid JSON (%s)" % (path, error))
+
+ def test_all_descriptions_have_reasonable_length(self):
+ for case in cases(self.test_files):
+ description = case["description"]
+ self.assertLess(
+ len(description),
+ 70,
+ "%r is too long! (keep it to less than 70 chars)" % (
+ description,
+ ),
+ )
+
+ def test_all_descriptions_are_unique(self):
+ for group in groups(self.test_files):
+ descriptions = set(test["description"] for test in group["tests"])
+ self.assertEqual(
+ len(descriptions),
+ len(group["tests"]),
+ "%r contains a duplicate description" % (group,)
+ )
+
+ @unittest.skipIf(jsonschema is None, "Validation library not present!")
+ def test_all_schemas_are_valid(self):
+ for schema in os.listdir(SUITE_ROOT_DIR):
+ schema_validator = jsonschema.validators.validators.get(schema)
+ if schema_validator is not None:
+ test_files = collect(os.path.join(SUITE_ROOT_DIR, schema))
+ for case in cases(test_files):
+ try:
+ schema_validator.check_schema(case["schema"])
+ except jsonschema.SchemaError as error:
+ self.fail("%s contains an invalid schema (%s)" %
+ (case, error))
+ else:
+ warnings.warn("No schema validator for %s" % schema)
+
+ @unittest.skipIf(jsonschema is None, "Validation library not present!")
+ def test_suites_are_valid(self):
+ Validator = jsonschema.validators.validator_for(TESTSUITE_SCHEMA)
+ validator = Validator(TESTSUITE_SCHEMA)
+ for tests in files(self.test_files):
+ try:
+ validator.validate(tests)
+ except jsonschema.ValidationError as error:
+ self.fail(str(error))
+
+
+def main(arguments):
+ if arguments.command == "check":
+ suite = unittest.TestLoader().loadTestsFromTestCase(SanityTests)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ sys.exit(not result.wasSuccessful())
+ elif arguments.command == "flatten":
+ selected_cases = [case for case in cases(collect(arguments.version))]
+
+ if arguments.randomize:
+ random.shuffle(selected_cases)
+
+ json.dump(selected_cases, sys.stdout, indent=4, sort_keys=True)
+ elif arguments.command == "remotes":
+ remotes = {}
+ for path in collect(REMOTES_DIR):
+ relative_path = os.path.relpath(path, REMOTES_DIR)
+ with open(path) as schema_file:
+ remotes[relative_path] = json.load(schema_file)
+ json.dump(remotes, sys.stdout, indent=4, sort_keys=True)
+ elif arguments.command == "dump_remotes":
+ if arguments.update:
+ shutil.rmtree(arguments.out_dir, ignore_errors=True)
+
+ try:
+ shutil.copytree(REMOTES_DIR, arguments.out_dir)
+ except OSError as e:
+ if e.errno == errno.EEXIST:
+ print("%s already exists. Aborting." % arguments.out_dir)
+ sys.exit(1)
+ raise
+ elif arguments.command == "serve":
+ try:
+ import flask
+ except ImportError:
+ print(textwrap.dedent("""
+ The Flask library is required to serve the remote schemas.
+
+ You can install it by running `pip install Flask`.
+
+ Alternatively, see the `jsonschema_suite remotes` or
+ `jsonschema_suite dump_remotes` commands to create static files
+ that can be served with your own web server.
+ """.strip("\n")))
+ sys.exit(1)
+
+ app = flask.Flask(__name__)
+
+ @app.route("/<path:path>")
+ def serve_path(path):
+ return flask.send_from_directory(REMOTES_DIR, path)
+
+ app.run(port=1234)
+
+
+parser = argparse.ArgumentParser(
+ description="JSON Schema Test Suite utilities",
+)
+subparsers = parser.add_subparsers(
+ help="utility commands", dest="command", metavar="COMMAND"
+)
+subparsers.required = True
+
+check = subparsers.add_parser("check", help="Sanity check the test suite.")
+
+flatten = subparsers.add_parser(
+ "flatten",
+ help="Output a flattened file containing a selected version's test cases."
+)
+flatten.add_argument(
+ "--randomize",
+ action="store_true",
+ help="Randomize the order of the outputted cases.",
+)
+flatten.add_argument(
+ "version", help="The directory containing the version to output",
+)
+
+remotes = subparsers.add_parser(
+ "remotes",
+ help="Output the expected URLs and their associated schemas for remote "
+ "ref tests as a JSON object."
+)
+
+dump_remotes = subparsers.add_parser(
+ "dump_remotes", help="Dump the remote ref schemas into a file tree",
+)
+dump_remotes.add_argument(
+ "--update",
+ action="store_true",
+ help="Update the remotes in an existing directory.",
+)
+dump_remotes.add_argument(
+ "--out-dir",
+ default=REMOTES_DIR,
+ type=os.path.abspath,
+ help="The output directory to create as the root of the file tree",
+)
+
+serve = subparsers.add_parser(
+ "serve",
+ help="Start a webserver to serve schemas used by remote ref tests."
+)
+
+if __name__ == "__main__":
+ main(parser.parse_args())
diff --git a/json/index.js b/json/index.js
new file mode 100644
index 0000000..7d01093
--- /dev/null
+++ b/json/index.js
@@ -0,0 +1,46 @@
+'use strict';
+
+const Ajv = require('ajv');
+const jsonSchemaTest = require('json-schema-test');
+
+const refs = {
+ 'http://localhost:1234/integer.json': require('./remotes/integer.json'),
+ 'http://localhost:1234/subSchemas.json': require('./remotes/subSchemas.json'),
+ 'http://localhost:1234/baseUriChange/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'),
+ 'http://localhost:1234/baseUriChangeFolder/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'),
+ 'http://localhost:1234/baseUriChangeFolderInSubschema/folderInteger.json': require('./remotes/baseUriChange/folderInteger.json'),
+ 'http://localhost:1234/name.json': require('./remotes/name.json'),
+ 'http://localhost:1234/name-defs.json': require('./remotes/name-defs.json')
+};
+
+const SKIP = {
+ 4: ['optional/zeroTerminatedFloats'],
+ 7: [
+ 'format/idn-email',
+ 'format/idn-hostname',
+ 'format/iri',
+ 'format/iri-reference',
+ 'optional/content'
+ ]
+};
+
+[4, 6, 7].forEach((draft) => {
+ let ajv;
+ if (draft == 7) {
+ ajv = new Ajv({format: 'full'});
+ } else {
+ const schemaId = draft == 4 ? 'id' : '$id';
+ ajv = new Ajv({format: 'full', meta: false, schemaId});
+ ajv.addMetaSchema(require(`ajv/lib/refs/json-schema-draft-0${draft}.json`));
+ ajv._opts.defaultMeta = `http://json-schema.org/draft-0${draft}/schema#`;
+ }
+ for (const uri in refs) ajv.addSchema(refs[uri], uri);
+
+ jsonSchemaTest(ajv, {
+ description: `Test suite draft-0${draft}`,
+ suites: {tests: `./tests/draft${draft}/{**/,}*.json`},
+ skip: SKIP[draft],
+ cwd: __dirname,
+ hideFolder: 'tests/'
+ });
+});
diff --git a/json/package.json b/json/package.json
new file mode 100644
index 0000000..3980136
--- /dev/null
+++ b/json/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "json-schema-test-suite",
+ "version": "0.1.0",
+ "description": "A language agnostic test suite for the JSON Schema specifications",
+ "main": "index.js",
+ "scripts": {
+ "test": "mocha index.js -R spec"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/json-schema-org/JSON-Schema-Test-Suite.git"
+ },
+ "keywords": [
+ "json-schema",
+ "tests"
+ ],
+ "author": "http://json-schema.org",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/json-schema-org/JSON-Schema-Test-Suite/issues"
+ },
+ "homepage": "https://github.com/json-schema-org/JSON-Schema-Test-Suite#readme",
+ "devDependencies": {
+ "ajv": "^6.0.0-rc.1",
+ "json-schema-test": "^2.0.0",
+ "mocha": "^3.2.0"
+ }
+}
diff --git a/json/remotes/baseUriChange/folderInteger.json b/json/remotes/baseUriChange/folderInteger.json
new file mode 100644
index 0000000..8b50ea3
--- /dev/null
+++ b/json/remotes/baseUriChange/folderInteger.json
@@ -0,0 +1,3 @@
+{
+ "type": "integer"
+}
diff --git a/json/remotes/baseUriChangeFolder/folderInteger.json b/json/remotes/baseUriChangeFolder/folderInteger.json
new file mode 100644
index 0000000..8b50ea3
--- /dev/null
+++ b/json/remotes/baseUriChangeFolder/folderInteger.json
@@ -0,0 +1,3 @@
+{
+ "type": "integer"
+}
diff --git a/json/remotes/baseUriChangeFolderInSubschema/folderInteger.json b/json/remotes/baseUriChangeFolderInSubschema/folderInteger.json
new file mode 100644
index 0000000..8b50ea3
--- /dev/null
+++ b/json/remotes/baseUriChangeFolderInSubschema/folderInteger.json
@@ -0,0 +1,3 @@
+{
+ "type": "integer"
+}
diff --git a/json/remotes/draft-next/format-assertion-false.json b/json/remotes/draft-next/format-assertion-false.json
new file mode 100644
index 0000000..1a55fed
--- /dev/null
+++ b/json/remotes/draft-next/format-assertion-false.json
@@ -0,0 +1,12 @@
+{
+ "$id": "http://localhost:1234/draft-next/format-assertion-false.json",
+ "$schema": "https://json-schema.org/draft/next/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/next/vocab/core": true,
+ "https://json-schema.org/draft/next/vocab/format-assertion": false
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/next/schema/meta/core" },
+ { "$ref": "https://json-schema.org/draft/next/schema/meta/format-assertion" }
+ ]
+}
diff --git a/json/remotes/draft-next/format-assertion-true.json b/json/remotes/draft-next/format-assertion-true.json
new file mode 100644
index 0000000..5862dfe
--- /dev/null
+++ b/json/remotes/draft-next/format-assertion-true.json
@@ -0,0 +1,12 @@
+{
+ "$id": "http://localhost:1234/draft-next/format-assertion-true.json",
+ "$schema": "https://json-schema.org/draft/next/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/next/vocab/core": true,
+ "https://json-schema.org/draft/next/vocab/format-assertion": true
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/next/schema/meta/core" },
+ { "$ref": "https://json-schema.org/draft/next/schema/meta/format-assertion" }
+ ]
+}
diff --git a/json/remotes/draft-next/metaschema-no-validation.json b/json/remotes/draft-next/metaschema-no-validation.json
new file mode 100644
index 0000000..2b50c05
--- /dev/null
+++ b/json/remotes/draft-next/metaschema-no-validation.json
@@ -0,0 +1,11 @@
+{
+ "$id": "http://localhost:1234/draft-next/metaschema-no-validation.json",
+ "$vocabulary": {
+ "https://json-schema.org/draft/next/vocab/applicator": true,
+ "https://json-schema.org/draft/next/vocab/core": true
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/next/meta/applicator" },
+ { "$ref": "https://json-schema.org/draft/next/meta/core" }
+ ]
+}
diff --git a/json/remotes/draft2019-09/metaschema-no-validation.json b/json/remotes/draft2019-09/metaschema-no-validation.json
new file mode 100644
index 0000000..9a54944
--- /dev/null
+++ b/json/remotes/draft2019-09/metaschema-no-validation.json
@@ -0,0 +1,11 @@
+{
+ "$id": "http://localhost:1234/draft2019-09/metaschema-no-validation.json",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2019-09/vocab/applicator": true,
+ "https://json-schema.org/draft/2019-09/vocab/core": true
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/2019-09/meta/applicator" },
+ { "$ref": "https://json-schema.org/draft/2019-09/meta/core" }
+ ]
+}
diff --git a/json/remotes/draft2020-12/format-assertion-false.json b/json/remotes/draft2020-12/format-assertion-false.json
new file mode 100644
index 0000000..f1c64d2
--- /dev/null
+++ b/json/remotes/draft2020-12/format-assertion-false.json
@@ -0,0 +1,12 @@
+{
+ "$id": "http://localhost:1234/draft2020-12/format-assertion-false.json",
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/core": true,
+ "https://json-schema.org/draft/2020-12/vocab/format-assertion": false
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/2020-12/schema/meta/core" },
+ { "$ref": "https://json-schema.org/draft/2020-12/schema/meta/format-assertion" }
+ ]
+}
diff --git a/json/remotes/draft2020-12/format-assertion-true.json b/json/remotes/draft2020-12/format-assertion-true.json
new file mode 100644
index 0000000..e3d12c8
--- /dev/null
+++ b/json/remotes/draft2020-12/format-assertion-true.json
@@ -0,0 +1,12 @@
+{
+ "$id": "http://localhost:1234/draft2020-12/format-assertion-true.json",
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/core": true,
+ "https://json-schema.org/draft/2020-12/vocab/format-assertion": true
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/2020-12/schema/meta/core" },
+ { "$ref": "https://json-schema.org/draft/2020-12/schema/meta/format-assertion" }
+ ]
+}
diff --git a/json/remotes/draft2020-12/metaschema-no-validation.json b/json/remotes/draft2020-12/metaschema-no-validation.json
new file mode 100644
index 0000000..d71f440
--- /dev/null
+++ b/json/remotes/draft2020-12/metaschema-no-validation.json
@@ -0,0 +1,11 @@
+{
+ "$id": "http://localhost:1234/draft2020-12/metaschema-no-validation.json",
+ "$vocabulary": {
+ "https://json-schema.org/draft/2020-12/vocab/applicator": true,
+ "https://json-schema.org/draft/2020-12/vocab/core": true
+ },
+ "allOf": [
+ { "$ref": "https://json-schema.org/draft/2020-12/meta/applicator" },
+ { "$ref": "https://json-schema.org/draft/2020-12/meta/core" }
+ ]
+}
diff --git a/json/remotes/extendible-dynamic-ref.json b/json/remotes/extendible-dynamic-ref.json
new file mode 100644
index 0000000..d0bcd37
--- /dev/null
+++ b/json/remotes/extendible-dynamic-ref.json
@@ -0,0 +1,20 @@
+{
+ "description": "extendible array",
+ "$id": "http://localhost:1234/extendible-dynamic-ref.json",
+ "type": "object",
+ "properties": {
+ "elements": {
+ "type": "array",
+ "items": {
+ "$dynamicRef": "#elements"
+ }
+ }
+ },
+ "required": ["elements"],
+ "additionalProperties": false,
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements"
+ }
+ }
+}
diff --git a/json/remotes/integer.json b/json/remotes/integer.json
new file mode 100644
index 0000000..8b50ea3
--- /dev/null
+++ b/json/remotes/integer.json
@@ -0,0 +1,3 @@
+{
+ "type": "integer"
+}
diff --git a/json/remotes/name-defs.json b/json/remotes/name-defs.json
new file mode 100644
index 0000000..1dab4a4
--- /dev/null
+++ b/json/remotes/name-defs.json
@@ -0,0 +1,15 @@
+{
+ "$defs": {
+ "orNull": {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#"
+ }
+ ]
+ }
+ },
+ "type": "string"
+}
diff --git a/json/remotes/name.json b/json/remotes/name.json
new file mode 100644
index 0000000..fceacb8
--- /dev/null
+++ b/json/remotes/name.json
@@ -0,0 +1,15 @@
+{
+ "definitions": {
+ "orNull": {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "$ref": "#"
+ }
+ ]
+ }
+ },
+ "type": "string"
+}
diff --git a/json/remotes/ref-and-definitions.json b/json/remotes/ref-and-definitions.json
new file mode 100644
index 0000000..e0ee802
--- /dev/null
+++ b/json/remotes/ref-and-definitions.json
@@ -0,0 +1,11 @@
+{
+ "$id": "http://localhost:1234/ref-and-definitions.json",
+ "definitions": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "allOf": [ { "$ref": "#/definitions/inner" } ]
+}
diff --git a/json/remotes/ref-and-defs.json b/json/remotes/ref-and-defs.json
new file mode 100644
index 0000000..85d06c3
--- /dev/null
+++ b/json/remotes/ref-and-defs.json
@@ -0,0 +1,11 @@
+{
+ "$id": "http://localhost:1234/ref-and-defs.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+}
diff --git a/json/remotes/subSchemas-defs.json b/json/remotes/subSchemas-defs.json
new file mode 100644
index 0000000..50b7b6d
--- /dev/null
+++ b/json/remotes/subSchemas-defs.json
@@ -0,0 +1,10 @@
+{
+ "$defs": {
+ "integer": {
+ "type": "integer"
+ },
+ "refToInteger": {
+ "$ref": "#/$defs/integer"
+ }
+ }
+}
diff --git a/json/remotes/subSchemas.json b/json/remotes/subSchemas.json
new file mode 100644
index 0000000..9f8030b
--- /dev/null
+++ b/json/remotes/subSchemas.json
@@ -0,0 +1,8 @@
+{
+ "integer": {
+ "type": "integer"
+ },
+ "refToInteger": {
+ "$ref": "#/integer"
+ }
+}
diff --git a/json/remotes/tree.json b/json/remotes/tree.json
new file mode 100644
index 0000000..a12d98b
--- /dev/null
+++ b/json/remotes/tree.json
@@ -0,0 +1,16 @@
+{
+ "description": "tree schema, extensible",
+ "$id": "http://localhost:1234/tree.json",
+ "$dynamicAnchor": "node",
+
+ "type": "object",
+ "properties": {
+ "data": true,
+ "children": {
+ "type": "array",
+ "items": {
+ "$dynamicRef": "#node"
+ }
+ }
+ }
+}
diff --git a/json/test-schema.json b/json/test-schema.json
new file mode 100644
index 0000000..11b1e8d
--- /dev/null
+++ b/json/test-schema.json
@@ -0,0 +1,91 @@
+{
+ "$schema": "http://json-schema.org/draft-06/schema#",
+ "description": "Schema for tests",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [ "description", "schema", "tests" ],
+ "properties": {
+ "id": {
+ "description": "Uniquely identifies a set of tests",
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "description": "The test set description",
+ "type": "string"
+ },
+ "comment": {
+ "description": "Any additional comments about the test set",
+ "type": "string"
+ },
+ "schema": {
+ "description": "This should be a valid schema. This should be a ref to a meta-schema if schema keywords need testing."
+ },
+ "tests": {
+ "description": "A set of related tests all using the same schema",
+ "type": "array",
+ "items": { "$ref": "#/definitions/test" },
+ "minItems": 1
+ }
+ },
+ "additionalProperties": false,
+ "minItems": 1
+ },
+ "definitions": {
+ "outputItem": {
+ "type": "object",
+ "properties": {
+ "valid": { "type": "boolean" },
+ "keywordLocation": { "type": "string" },
+ "absoluteKeywordLocation": {
+ "type": "string",
+ "format": "uri"
+ },
+ "instanceLocation": { "type": "string" },
+ "annotations": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/outputItem" }
+ },
+ "errors": {
+ "type": "array",
+ "items": { "$ref": "#/definitions/outputItem" }
+ }
+ }
+ },
+ "test": {
+ "description": "A single test",
+ "type": "object",
+ "required": [ "description", "data", "valid" ],
+ "properties": {
+ "id": {
+ "description": "Uniquely identifies a single test",
+ "type": "string",
+ "format": "uri"
+ },
+ "description": {
+ "description": "The test description",
+ "type": "string"
+ },
+ "comment": {
+ "description": "Any additional comments about the test",
+ "type": "string"
+ },
+ "data": {
+ "description": "This is the instance to be validated against the schema in \"schema\"."
+ },
+ "valid": { "type": "boolean" },
+ "output": {
+ "type": "object",
+ "required": [ "basic", "detailed", "verbose" ],
+ "properties": {
+ "basic": { "$ref": "#/definitions/outputItem" },
+ "detailed": { "$ref": "#/definitions/outputItem" },
+ "verbose": { "$ref": "#/definitions/outputItem" }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/json/tests/draft-next/additionalProperties.json b/json/tests/draft-next/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft-next/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/allOf.json b/json/tests/draft-next/allOf.json
new file mode 100644
index 0000000..ec9319e
--- /dev/null
+++ b/json/tests/draft-next/allOf.json
@@ -0,0 +1,294 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all true",
+ "schema": {"allOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, some false",
+ "schema": {"allOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all false",
+ "schema": {"allOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/anchor.json b/json/tests/draft-next/anchor.json
new file mode 100644
index 0000000..416c224
--- /dev/null
+++ b/json/tests/draft-next/anchor.json
@@ -0,0 +1,173 @@
+[
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with absolute URI",
+ "schema": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar",
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$anchor inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $anchor buried in the enum",
+ "schema": {
+ "$defs": {
+ "anchor_in_enum": {
+ "enum": [
+ {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ ]
+ },
+ "real_identifier_in_schema": {
+ "$anchor": "my_anchor",
+ "type": "string"
+ },
+ "zzz_anchor_in_const": {
+ "const": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/anchor_in_enum" },
+ { "$ref": "#my_anchor" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "in implementations that strip $anchor, this may match either $def",
+ "data": {
+ "type": "null"
+ },
+ "valid": false
+ },
+ {
+ "description": "match $ref to $anchor",
+ "data": "a string to match #/$defs/anchor_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $anchor",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "same $anchor with different base uri",
+ "schema": {
+ "$id": "http://localhost:1234/foobar",
+ "$defs": {
+ "A": {
+ "$id": "child1",
+ "allOf": [
+ {
+ "$id": "child2",
+ "$anchor": "my_anchor",
+ "type": "number"
+ },
+ {
+ "$anchor": "my_anchor",
+ "type": "string"
+ }
+ ]
+ }
+ },
+ "$ref": "child1#my_anchor"
+ },
+ "tests": [
+ {
+ "description": "$ref should resolve to /$defs/A/allOf/1",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "$ref should not resolve to /$defs/A/allOf/0",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/anyOf.json b/json/tests/draft-next/anyOf.json
new file mode 100644
index 0000000..ab5eb38
--- /dev/null
+++ b/json/tests/draft-next/anyOf.json
@@ -0,0 +1,189 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all true",
+ "schema": {"anyOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, some true",
+ "schema": {"anyOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all false",
+ "schema": {"anyOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/boolean_schema.json b/json/tests/draft-next/boolean_schema.json
new file mode 100644
index 0000000..6d40f23
--- /dev/null
+++ b/json/tests/draft-next/boolean_schema.json
@@ -0,0 +1,104 @@
+[
+ {
+ "description": "boolean schema 'true'",
+ "schema": true,
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "boolean true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "boolean false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean schema 'false'",
+ "schema": false,
+ "tests": [
+ {
+ "description": "number is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "boolean true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "boolean false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/const.json b/json/tests/draft-next/const.json
new file mode 100644
index 0000000..1c2cafc
--- /dev/null
+++ b/json/tests/draft-next/const.json
@@ -0,0 +1,342 @@
+[
+ {
+ "description": "const validation",
+ "schema": {"const": 2},
+ "tests": [
+ {
+ "description": "same value is valid",
+ "data": 2,
+ "valid": true
+ },
+ {
+ "description": "another value is invalid",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with object",
+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
+ "tests": [
+ {
+ "description": "same object is valid",
+ "data": {"foo": "bar", "baz": "bax"},
+ "valid": true
+ },
+ {
+ "description": "same object with different property order is valid",
+ "data": {"baz": "bax", "foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "another object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": [1, 2],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with array",
+ "schema": {"const": [{ "foo": "bar" }]},
+ "tests": [
+ {
+ "description": "same array is valid",
+ "data": [{"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "another array item is invalid",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "array with additional items is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with null",
+ "schema": {"const": null},
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "not null is invalid",
+ "data": 0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with false does not match 0",
+ "schema": {"const": false},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with true does not match 1",
+ "schema": {"const": true},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [false] does not match [0]",
+ "schema": {"const": [false]},
+ "tests": [
+ {
+ "description": "[false] is valid",
+ "data": [false],
+ "valid": true
+ },
+ {
+ "description": "[0] is invalid",
+ "data": [0],
+ "valid": false
+ },
+ {
+ "description": "[0.0] is invalid",
+ "data": [0.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [true] does not match [1]",
+ "schema": {"const": [true]},
+ "tests": [
+ {
+ "description": "[true] is valid",
+ "data": [true],
+ "valid": true
+ },
+ {
+ "description": "[1] is invalid",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "[1.0] is invalid",
+ "data": [1.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": false} does not match {\"a\": 0}",
+ "schema": {"const": {"a": false}},
+ "tests": [
+ {
+ "description": "{\"a\": false} is valid",
+ "data": {"a": false},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 0} is invalid",
+ "data": {"a": 0},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 0.0} is invalid",
+ "data": {"a": 0.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": true} does not match {\"a\": 1}",
+ "schema": {"const": {"a": true}},
+ "tests": [
+ {
+ "description": "{\"a\": true} is valid",
+ "data": {"a": true},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 1} is invalid",
+ "data": {"a": 1},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 1.0} is invalid",
+ "data": {"a": 1.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 0 does not match other zero-like types",
+ "schema": {"const": 0},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "empty string is invalid",
+ "data": "",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 1 does not match true",
+ "schema": {"const": 1},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "const with -2.0 matches integer and float types",
+ "schema": {"const": -2.0},
+ "tests": [
+ {
+ "description": "integer -2 is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "integer 2 is invalid",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "float -2.0 is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float 2.0 is invalid",
+ "data": 2.0,
+ "valid": false
+ },
+ {
+ "description": "float -2.00001 is invalid",
+ "data": -2.00001,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "float and integers are equal up to 64-bit representation limits",
+ "schema": {"const": 9007199254740992},
+ "tests": [
+ {
+ "description": "integer is valid",
+ "data": 9007199254740992,
+ "valid": true
+ },
+ {
+ "description": "integer minus one is invalid",
+ "data": 9007199254740991,
+ "valid": false
+ },
+ {
+ "description": "float is valid",
+ "data": 9007199254740992.0,
+ "valid": true
+ },
+ {
+ "description": "float minus one is invalid",
+ "data": 9007199254740991.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "const": "hello\u0000there" },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/contains.json b/json/tests/draft-next/contains.json
new file mode 100644
index 0000000..4fca81a
--- /dev/null
+++ b/json/tests/draft-next/contains.json
@@ -0,0 +1,241 @@
+[
+ {
+ "description": "contains keyword validation",
+ "schema": {
+ "contains": { "minimum": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item matching schema (5) is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with item matching schema (6) is valid",
+ "data": [3, 4, 6],
+ "valid": true
+ },
+ {
+ "description": "array with two items matching schema (5, 6) is valid",
+ "data": [3, 4, 5, 6],
+ "valid": true
+ },
+ {
+ "description": "array without items matching schema is invalid",
+ "data": [2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "object with property matching schema (5) is valid",
+ "data": { "a": 3, "b": 4, "c": 5 },
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema (6) is valid",
+ "data": { "a": 3, "b": 4, "c": 6 },
+ "valid": true
+ },
+ {
+ "description": "object with two properties matching schema (5, 6) is valid",
+ "data": { "a": 3, "b": 4, "c": 5, "d": 6 },
+ "valid": true
+ },
+ {
+ "description": "object without properties matching schema is invalid",
+ "data": { "a": 2, "b": 3, "c": 4 },
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "not array or object is valid",
+ "data": 42,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with const keyword",
+ "schema": {
+ "contains": { "const": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item 5 is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with two items 5 is valid",
+ "data": [3, 4, 5, 5],
+ "valid": true
+ },
+ {
+ "description": "array without item 5 is invalid",
+ "data": [1, 2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "object with property 5 is valid",
+ "data": { "a": 3, "b": 4, "c": 5 },
+ "valid": true
+ },
+ {
+ "description": "object with two properties 5 is valid",
+ "data": { "a": 3, "b": 4, "c": 5, "d": 5 },
+ "valid": true
+ },
+ {
+ "description": "object without property 5 is invalid",
+ "data": { "a": 1, "b": 2, "c": 3, "d": 4 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema true",
+ "schema": { "contains": true },
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "any non-empty object is valid",
+ "data": { "a": "foo" },
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema false",
+ "schema": { "contains": false },
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "any non-empty object is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "non-arrays/objects are valid",
+ "data": "contains does not apply to strings",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items + contains",
+ "schema": {
+ "additionalProperties": { "multipleOf": 2 },
+ "items": { "multipleOf": 2 },
+ "contains": { "multipleOf": 3 }
+ },
+ "tests": [
+ {
+ "description": "matches items, does not match contains",
+ "data": [2, 4, 8],
+ "valid": false
+ },
+ {
+ "description": "does not match items, matches contains",
+ "data": [3, 6, 9],
+ "valid": false
+ },
+ {
+ "description": "matches both items and contains",
+ "data": [6, 12],
+ "valid": true
+ },
+ {
+ "description": "matches neither items nor contains",
+ "data": [1, 5],
+ "valid": false
+ },
+ {
+ "description": "matches additionalProperties, does not match contains",
+ "data": { "a": 2, "b": 4, "c": 8 },
+ "valid": false
+ },
+ {
+ "description": "does not match additionalProperties, matches contains",
+ "data": { "a": 3, "b": 6, "c": 9 },
+ "valid": false
+ },
+ {
+ "description": "matches both additionalProperties and contains",
+ "data": { "a": 6, "b": 12 },
+ "valid": true
+ },
+ {
+ "description": "matches neither additionalProperties nor contains",
+ "data": { "a": 1, "b": 5 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains with false if subschema",
+ "schema": {
+ "contains": {
+ "if": false,
+ "else": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "any non-empty object is valid",
+ "data": { "a": "foo" },
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/content.json b/json/tests/draft-next/content.json
new file mode 100644
index 0000000..44688e8
--- /dev/null
+++ b/json/tests/draft-next/content.json
@@ -0,0 +1,127 @@
+[
+ {
+ "description": "validation of string-encoded content based on media type",
+ "schema": {
+ "contentMediaType": "application/json"
+ },
+ "tests": [
+ {
+ "description": "a valid JSON document",
+ "data": "{\"foo\": \"bar\"}",
+ "valid": true
+ },
+ {
+ "description": "an invalid JSON document; validates true",
+ "data": "{:}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary string-encoding",
+ "schema": {
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64 string",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string (% is not a valid character); validates true",
+ "data": "eyJmb28iOi%iYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents with schema",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64",
+ "contentSchema": { "required": ["foo"], "properties": { "foo": { "type": "string" } } }
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "another valid base64-encoded JSON document",
+ "data": "eyJib28iOiAyMCwgImZvbyI6ICJiYXoifQ==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64-encoded JSON document; validates true",
+ "data": "eyJib28iOiAyMH0=",
+ "valid": true
+ },
+ {
+ "description": "an empty object as a base64-encoded JSON document; validates true",
+ "data": "e30=",
+ "valid": true
+ },
+ {
+ "description": "an empty array as a base64-encoded JSON document",
+ "data": "W10=",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/default.json b/json/tests/draft-next/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft-next/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/defs.json b/json/tests/draft-next/defs.json
new file mode 100644
index 0000000..1282164
--- /dev/null
+++ b/json/tests/draft-next/defs.json
@@ -0,0 +1,20 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/next/schema"
+ },
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {"$defs": {"foo": {"type": "integer"}}},
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {"$defs": {"foo": {"type": 1}}},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/dependentRequired.json b/json/tests/draft-next/dependentRequired.json
new file mode 100644
index 0000000..c817120
--- /dev/null
+++ b/json/tests/draft-next/dependentRequired.json
@@ -0,0 +1,142 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {"dependentRequired": {"bar": ["foo"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "empty dependents",
+ "schema": {"dependentRequired": {"bar": []}},
+ "tests": [
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "object with one property",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "non-object is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependents required",
+ "schema": {"dependentRequired": {"quux": ["foo", "bar"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentRequired": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "CRLF",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quotes",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "CRLF missing dependent",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quotes missing dependent",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/dependentSchemas.json b/json/tests/draft-next/dependentSchemas.json
new file mode 100644
index 0000000..2ba1a75
--- /dev/null
+++ b/json/tests/draft-next/dependentSchemas.json
@@ -0,0 +1,129 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {
+ "dependentSchemas": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean subschemas",
+ "schema": {
+ "dependentSchemas": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property having schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property having schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentSchemas": {
+ "foo\tbar": {"minProperties": 4},
+ "foo'bar": {"required": ["foo\"bar"]}
+ }
+ },
+ "tests": [
+ {
+ "description": "quoted tab",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quote",
+ "data": {
+ "foo'bar": {"foo\"bar": 1}
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted tab invalid under dependent schema",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quote invalid under dependent schema",
+ "data": {"foo'bar": 1},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/dynamicRef.json b/json/tests/draft-next/dynamicRef.json
new file mode 100644
index 0000000..d022856
--- /dev/null
+++ b/json/tests/draft-next/dynamicRef.json
@@ -0,0 +1,476 @@
+[
+ {
+ "description": "A $dynamicRef to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamicRef-dynamicAnchor-same-schema/root",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $ref to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/ref-dynamicAnchor-same-schema/root",
+ "type": "array",
+ "items": { "$ref": "#items" },
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef should resolve to the first $dynamicAnchor still in scope that is encountered when the schema is evaluated",
+ "schema": {
+ "$id": "https://test.json-schema.org/typical-dynamic-resolution/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef with intermediate scopes that don't include a matching $dynamicAnchor should not affect dynamic scope resolution",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-resolution-with-intermediate-scopes/root",
+ "$ref": "intermediate-scope",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "intermediate-scope": {
+ "$id": "intermediate-scope",
+ "$ref": "list"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "An $anchor with the same name as a $dynamicAnchor should not be used for dynamic scope resolution",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-resolution-ignores-anchors/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$anchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$dynamicAnchor": "items"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "Any array is valid",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef that initially resolves to a schema with a matching $dynamicAnchor should resolve to the first $dynamicAnchor in the dynamic scope",
+ "schema": {
+ "$id": "https://test.json-schema.org/relative-dynamic-reference/root",
+ "$dynamicAnchor": "meta",
+ "type": "object",
+ "properties": {
+ "foo": { "const": "pass" }
+ },
+ "$ref": "extended",
+ "$defs": {
+ "extended": {
+ "$id": "extended",
+ "$dynamicAnchor": "meta",
+ "type": "object",
+ "properties": {
+ "bar": { "$ref": "bar" }
+ }
+ },
+ "bar": {
+ "$id": "bar",
+ "type": "object",
+ "properties": {
+ "baz": { "$dynamicRef": "extended#meta" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "The recursive part is valid against the root",
+ "data": {
+ "foo": "pass",
+ "bar": {
+ "baz": { "foo": "pass" }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "The recursive part is not valid against the root",
+ "data": {
+ "foo": "pass",
+ "bar": {
+ "baz": { "foo": "fail" }
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dynamic paths to the $dynamicRef keyword",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-ref-with-multiple-paths/main",
+ "$defs": {
+ "inner": {
+ "$id": "inner",
+ "$dynamicAnchor": "foo",
+ "title": "inner",
+ "additionalProperties": {
+ "$dynamicRef": "#foo"
+ }
+ }
+ },
+ "if": {
+ "propertyNames": {
+ "pattern": "^[a-m]"
+ }
+ },
+ "then": {
+ "title": "any type of node",
+ "$id": "anyLeafNode",
+ "$dynamicAnchor": "foo",
+ "$ref": "inner"
+ },
+ "else": {
+ "title": "integer node",
+ "$id": "integerNode",
+ "$dynamicAnchor": "foo",
+ "type": [ "object", "integer" ],
+ "$ref": "inner"
+ }
+ },
+ "tests": [
+ {
+ "description": "recurse to anyLeafNode - floats are allowed",
+ "data": { "alpha": 1.1 },
+ "valid": true
+ },
+ {
+ "description": "recurse to integerNode - floats are not allowed",
+ "data": { "november": 1.1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "after leaving a dynamic scope, it should not be used by a $dynamicRef",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-ref-leaving-dynamic-scope/main",
+ "if": {
+ "$id": "first_scope",
+ "$defs": {
+ "thingy": {
+ "$comment": "this is first_scope#thingy",
+ "$dynamicAnchor": "thingy",
+ "type": "number"
+ }
+ }
+ },
+ "then": {
+ "$id": "second_scope",
+ "$ref": "start",
+ "$defs": {
+ "thingy": {
+ "$comment": "this is second_scope#thingy, the final destination of the $dynamicRef",
+ "$dynamicAnchor": "thingy",
+ "type": "null"
+ }
+ }
+ },
+ "$defs": {
+ "start": {
+ "$comment": "this is the landing spot from $ref",
+ "$id": "start",
+ "$dynamicRef": "inner_scope#thingy"
+ },
+ "thingy": {
+ "$comment": "this is the first stop for the $dynamicRef",
+ "$id": "inner_scope",
+ "$dynamicAnchor": "thingy",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "string matches /$defs/thingy, but the $dynamicRef does not stop here",
+ "data": "a string",
+ "valid": false
+ },
+ {
+ "description": "first_scope is not in dynamic scope for the $dynamicRef",
+ "data": 42,
+ "valid": false
+ },
+ {
+ "description": "/then/$defs/thingy is the final stop for the $dynamicRef",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "strict-tree schema, guards against misspelled properties",
+ "schema": {
+ "$id": "http://localhost:1234/strict-tree.json",
+ "$dynamicAnchor": "node",
+
+ "$ref": "tree.json",
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "instance with misspelled field",
+ "data": {
+ "children": [{
+ "daat": 1
+ }]
+ },
+ "valid": false
+ },
+ {
+ "description": "instance with correct field",
+ "data": {
+ "children": [{
+ "data": 1
+ }]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "tests for implementation dynamic anchor and reference link",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible.json",
+ "$ref": "extendible-dynamic-ref.json",
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering.",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible-allof-defs-first.json",
+ "allOf": [
+ {
+ "$ref": "extendible-dynamic-ref.json"
+ },
+ {
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering.",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible-allof-ref-first.json",
+ "allOf": [
+ {
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ },
+ {
+ "$ref": "extendible-dynamic-ref.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/enum.json b/json/tests/draft-next/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft-next/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/exclusiveMaximum.json b/json/tests/draft-next/exclusiveMaximum.json
new file mode 100644
index 0000000..dc3cd70
--- /dev/null
+++ b/json/tests/draft-next/exclusiveMaximum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "exclusiveMaximum": 3.0
+ },
+ "tests": [
+ {
+ "description": "below the exclusiveMaximum is valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ },
+ {
+ "description": "above the exclusiveMaximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/exclusiveMinimum.json b/json/tests/draft-next/exclusiveMinimum.json
new file mode 100644
index 0000000..b38d7ec
--- /dev/null
+++ b/json/tests/draft-next/exclusiveMinimum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "exclusiveMinimum": 1.1
+ },
+ "tests": [
+ {
+ "description": "above the exclusiveMinimum is valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "below the exclusiveMinimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/format.json b/json/tests/draft-next/format.json
new file mode 100644
index 0000000..a4b51d2
--- /dev/null
+++ b/json/tests/draft-next/format.json
@@ -0,0 +1,686 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-email format",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "regex format",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-hostname format",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date format",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "time format",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "json-pointer format",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative-json-pointer format",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri format",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri-reference format",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-reference format",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-template format",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uuid format",
+ "schema": { "format": "uuid" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "duration format",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/id.json b/json/tests/draft-next/id.json
new file mode 100644
index 0000000..c5eab10
--- /dev/null
+++ b/json/tests/draft-next/id.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "Invalid use of fragments in location-independent $id",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/next/schema"
+ },
+ "tests": [
+ {
+ "description": "Identifier name",
+ "data": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name and no ref",
+ "data": {
+ "$defs": {
+ "A": { "$id": "#foo" }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path",
+ "data": {
+ "$ref": "#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Valid use of empty fragments in location-independent $id",
+ "comment": "These are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/next/schema"
+ },
+ "tests": [
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/$defs/B",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Unnormalized $ids are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/next/schema"
+ },
+ "tests": [
+ {
+ "description": "Unnormalized identifier",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $id buried in the enum",
+ "schema": {
+ "$defs": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_enum" },
+ { "$ref": "https://localhost:1234/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to $id",
+ "data": "a string to match #/$defs/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/if-then-else.json b/json/tests/draft-next/if-then-else.json
new file mode 100644
index 0000000..284e919
--- /dev/null
+++ b/json/tests/draft-next/if-then-else.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "ignore if without then or else",
+ "schema": {
+ "if": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone if",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone if",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore then without if",
+ "schema": {
+ "then": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone then",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone then",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore else without if",
+ "schema": {
+ "else": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone else",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone else",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and then without else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid when if test fails",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and else without then",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when if test passes",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validate against correct branch, then vs else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-interference across combined schemas",
+ "schema": {
+ "allOf": [
+ {
+ "if": {
+ "exclusiveMaximum": 0
+ }
+ },
+ {
+ "then": {
+ "minimum": -10
+ }
+ },
+ {
+ "else": {
+ "multipleOf": 2
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid, but would have been invalid through then",
+ "data": -100,
+ "valid": true
+ },
+ {
+ "description": "valid, but would have been invalid through else",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema true",
+ "schema": {
+ "if": true,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema true in if always chooses the then path (valid)",
+ "data": "then",
+ "valid": true
+ },
+ {
+ "description": "boolean schema true in if always chooses the then path (invalid)",
+ "data": "else",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema false",
+ "schema": {
+ "if": false,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema false in if always chooses the else path (invalid)",
+ "data": "then",
+ "valid": false
+ },
+ {
+ "description": "boolean schema false in if always chooses the else path (valid)",
+ "data": "else",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if appears at the end when serialized (keyword processing sequence)",
+ "schema": {
+ "then": { "const": "yes" },
+ "else": { "const": "other" },
+ "if": { "maxLength": 4 }
+ },
+ "tests": [
+ {
+ "description": "yes redirects to then and passes",
+ "data": "yes",
+ "valid": true
+ },
+ {
+ "description": "other redirects to else and passes",
+ "data": "other",
+ "valid": true
+ },
+ {
+ "description": "no redirects to then and fails",
+ "data": "no",
+ "valid": false
+ },
+ {
+ "description": "invalid redirects to else and fails",
+ "data": "invalid",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/infinite-loop-detection.json b/json/tests/draft-next/infinite-loop-detection.json
new file mode 100644
index 0000000..9c3c362
--- /dev/null
+++ b/json/tests/draft-next/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "$defs": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/items.json b/json/tests/draft-next/items.json
new file mode 100644
index 0000000..b918194
--- /dev/null
+++ b/json/tests/draft-next/items.json
@@ -0,0 +1,256 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (true)",
+ "schema": {"items": true},
+ "tests": [
+ {
+ "description": "any array is valid",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (false)",
+ "schema": {"items": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": [ 1, "foo", true ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "$defs": {
+ "item": {
+ "type": "array",
+ "items": false,
+ "prefixItems": [
+ { "$ref": "#/$defs/sub-item" },
+ { "$ref": "#/$defs/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "items": false,
+ "prefixItems": [
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "prefixItems with no additional items allowed",
+ "schema": {
+ "prefixItems": [{}, {}, {}],
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "prefixItems": [ { "minimum": 3 } ] }
+ ],
+ "items": { "minimum": 5 }
+ },
+ "tests": [
+ {
+ "description": "prefixItems in allOf should not constrain items, invalid case",
+ "data": [ 3, 5 ],
+ "valid": false
+ },
+ {
+ "description": "prefixItems in allOf should not constrain items, valid case",
+ "data": [ 5, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "prefixItems validation adjusts the starting index for items",
+ "schema": {
+ "prefixItems": [ { "type": "string" } ],
+ "items": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/maxContains.json b/json/tests/draft-next/maxContains.json
new file mode 100644
index 0000000..e95bb9f
--- /dev/null
+++ b/json/tests/draft-next/maxContains.json
@@ -0,0 +1,129 @@
+[
+ {
+ "description": "maxContains without contains is ignored",
+ "schema": {
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone maxContains",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "two items still valid against lone maxContains",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "one property valid against lone maxContains",
+ "data": { "a": 1 },
+ "valid": true
+ },
+ {
+ "description": "two properties still valid against lone maxContains",
+ "data": { "a": 1, "b": 2 },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains with contains",
+ "schema": {
+ "contains": { "const": 1 },
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "some elements match, valid maxContains",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "some elements match, invalid maxContains",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "all properties match, valid maxContains",
+ "data": { "a": 1 },
+ "valid": true
+ },
+ {
+ "description": "all properties match, invalid maxContains",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "some properties match, valid maxContains",
+ "data": { "a": 1, "b": 2 },
+ "valid": true
+ },
+ {
+ "description": "some properties match, invalid maxContains",
+ "data": { "a": 1, "b": 2, "c": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains < maxContains",
+ "schema": {
+ "contains": { "const": 1 },
+ "minContains": 1,
+ "maxContains": 3
+ },
+ "tests": [
+ {
+ "description": "array with actual < minContains < maxContains",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "array with minContains < actual < maxContains",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "array with minContains < maxContains < actual",
+ "data": [1, 1, 1, 1],
+ "valid": false
+ },
+ {
+ "description": "object with actual < minContains < maxContains",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "object with minContains < actual < maxContains",
+ "data": { "a": 1, "b": 1 },
+ "valid": true
+ },
+ {
+ "description": "object with minContains < maxContains < actual",
+ "data": { "a": 1, "b": 1, "c": 1, "d": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/maxItems.json b/json/tests/draft-next/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft-next/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/maxLength.json b/json/tests/draft-next/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft-next/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/maxProperties.json b/json/tests/draft-next/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft-next/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/maximum.json b/json/tests/draft-next/maximum.json
new file mode 100644
index 0000000..6844a39
--- /dev/null
+++ b/json/tests/draft-next/maximum.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/minContains.json b/json/tests/draft-next/minContains.json
new file mode 100644
index 0000000..287cfca
--- /dev/null
+++ b/json/tests/draft-next/minContains.json
@@ -0,0 +1,197 @@
+[
+ {
+ "description": "minContains without contains is ignored",
+ "schema": {
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone minContains",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "zero items still valid against lone minContains",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=1 with contains",
+ "schema": {
+ "contains": { "const": 1 },
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "no elements match",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "single element matches, valid minContains",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains",
+ "data": [1, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=2 with contains",
+ "schema": {
+ "contains": { "const": 1 },
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "some elements match, invalid minContains",
+ "data": [1, 2],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid minContains (exactly as needed)",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains (more than needed)",
+ "data": [1, 1, 1],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [1, 2, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains = minContains",
+ "schema": {
+ "contains": { "const": 1 },
+ "maxContains": 2,
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [1, 1, 1],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains and minContains",
+ "data": [1, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains < minContains",
+ "schema": {
+ "contains": { "const": 1 },
+ "maxContains": 1,
+ "minContains": 3
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "invalid minContains",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains",
+ "data": [1, 1, 1],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains and minContains",
+ "data": [1, 1],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0",
+ "schema": {
+ "contains": { "const": 1 },
+ "minContains": 0
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "minContains = 0 makes contains always pass",
+ "data": [2],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0 with maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 0,
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "not more than maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "too many",
+ "data": [ 1, 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/minItems.json b/json/tests/draft-next/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft-next/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/minLength.json b/json/tests/draft-next/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft-next/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/minProperties.json b/json/tests/draft-next/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft-next/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/minimum.json b/json/tests/draft-next/minimum.json
new file mode 100644
index 0000000..21ae50e
--- /dev/null
+++ b/json/tests/draft-next/minimum.json
@@ -0,0 +1,69 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/multipleOf.json b/json/tests/draft-next/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft-next/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/not.json b/json/tests/draft-next/not.json
new file mode 100644
index 0000000..98de0ed
--- /dev/null
+++ b/json/tests/draft-next/not.json
@@ -0,0 +1,117 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema true",
+ "schema": {"not": true},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema false",
+ "schema": {"not": false},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/oneOf.json b/json/tests/draft-next/oneOf.json
new file mode 100644
index 0000000..eeb7ae8
--- /dev/null
+++ b/json/tests/draft-next/oneOf.json
@@ -0,0 +1,274 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all true",
+ "schema": {"oneOf": [true, true, true]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, one true",
+ "schema": {"oneOf": [true, false, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, more than one true",
+ "schema": {"oneOf": [true, true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all false",
+ "schema": {"oneOf": [false, false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": true,
+ "baz": true
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": true
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/bignum.json b/json/tests/draft-next/optional/bignum.json
new file mode 100644
index 0000000..3f49226
--- /dev/null
+++ b/json/tests/draft-next/optional/bignum.json
@@ -0,0 +1,93 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "exclusiveMaximum": 972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "exclusiveMinimum": -972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/ecmascript-regex.json b/json/tests/draft-next/optional/ecmascript-regex.json
new file mode 100644
index 0000000..1beb0b3
--- /dev/null
+++ b/json/tests/draft-next/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/float-overflow.json b/json/tests/draft-next/optional/float-overflow.json
new file mode 100644
index 0000000..52ff982
--- /dev/null
+++ b/json/tests/draft-next/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "integer", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format-assertion.json b/json/tests/draft-next/optional/format-assertion.json
new file mode 100644
index 0000000..ede922a
--- /dev/null
+++ b/json/tests/draft-next/optional/format-assertion.json
@@ -0,0 +1,42 @@
+[
+ {
+ "description": "schema that uses custom metaschema with format-assertion: false",
+ "schema": {
+ "$id": "https://schema/using/format-assertion/false",
+ "$schema": "http://localhost:1234/draft-next/format-assertion-false.json",
+ "format": "ipv4"
+ },
+ "tests": [
+ {
+ "description": "format-assertion: false: valid string",
+ "data": "127.0.0.1",
+ "valid": true
+ },
+ {
+ "description": "format-assertion: false: invalid string",
+ "data": "not-an-ipv4",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "schema that uses custom metaschema with format-assertion: true",
+ "schema": {
+ "$id": "https://schema/using/format-assertion/true",
+ "$schema": "http://localhost:1234/draft-next/format-assertion-true.json",
+ "format": "ipv4"
+ },
+ "tests": [
+ {
+ "description": "format-assertion: true: valid string",
+ "data": "127.0.0.1",
+ "valid": true
+ },
+ {
+ "description": "format-assertion: true: invalid string",
+ "data": "not-an-ipv4",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/date-time.json b/json/tests/draft-next/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft-next/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/date.json b/json/tests/draft-next/optional/format/date.json
new file mode 100644
index 0000000..b4f61ee
--- /dev/null
+++ b/json/tests/draft-next/optional/format/date.json
@@ -0,0 +1,223 @@
+[
+ {
+ "description": "validation of date strings",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date string",
+ "data": "1963-06-19",
+ "valid": true
+ },
+ {
+ "description": "a valid date string with 31 days in January",
+ "data": "2020-01-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in January",
+ "data": "2020-01-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 28 days in February (normal)",
+ "data": "2021-02-28",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 29 days in February (normal)",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 29 days in February (leap)",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 30 days in February (leap)",
+ "data": "2020-02-30",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in March",
+ "data": "2020-03-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in March",
+ "data": "2020-03-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in April",
+ "data": "2020-04-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in April",
+ "data": "2020-04-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in May",
+ "data": "2020-05-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in May",
+ "data": "2020-05-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in June",
+ "data": "2020-06-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in June",
+ "data": "2020-06-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in July",
+ "data": "2020-07-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in July",
+ "data": "2020-07-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in August",
+ "data": "2020-08-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in August",
+ "data": "2020-08-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in September",
+ "data": "2020-09-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in September",
+ "data": "2020-09-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in October",
+ "data": "2020-10-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in October",
+ "data": "2020-10-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in November",
+ "data": "2020-11-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in November",
+ "data": "2020-11-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in December",
+ "data": "2020-12-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in December",
+ "data": "2020-12-32",
+ "valid": false
+ },
+ {
+ "description": "a invalid date string with invalid month",
+ "data": "2020-13-01",
+ "valid": false
+ },
+ {
+ "description": "an invalid date string",
+ "data": "06/19/1963",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350",
+ "valid": false
+ },
+ {
+ "description": "non-padded month dates are not valid",
+ "data": "1998-1-20",
+ "valid": false
+ },
+ {
+ "description": "non-padded day dates are not valid",
+ "data": "1998-01-1",
+ "valid": false
+ },
+ {
+ "description": "invalid month",
+ "data": "1998-13-01",
+ "valid": false
+ },
+ {
+ "description": "invalid month-day combination",
+ "data": "1998-04-31",
+ "valid": false
+ },
+ {
+ "description": "2021 is not a leap year",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "2020 is a leap year",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1963-06-1৪",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/duration.json b/json/tests/draft-next/optional/format/duration.json
new file mode 100644
index 0000000..741348a
--- /dev/null
+++ b/json/tests/draft-next/optional/format/duration.json
@@ -0,0 +1,128 @@
+[
+ {
+ "description": "validation of duration strings",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid duration string",
+ "data": "P4DT12H30M5S",
+ "valid": true
+ },
+ {
+ "description": "an invalid duration string",
+ "data": "PT1D",
+ "valid": false
+ },
+ {
+ "description": "no elements present",
+ "data": "P",
+ "valid": false
+ },
+ {
+ "description": "no time elements present",
+ "data": "P1YT",
+ "valid": false
+ },
+ {
+ "description": "no date or time elements present",
+ "data": "PT",
+ "valid": false
+ },
+ {
+ "description": "elements out of order",
+ "data": "P2D1Y",
+ "valid": false
+ },
+ {
+ "description": "missing time separator",
+ "data": "P1D2H",
+ "valid": false
+ },
+ {
+ "description": "time element in the date position",
+ "data": "P2S",
+ "valid": false
+ },
+ {
+ "description": "four years duration",
+ "data": "P4Y",
+ "valid": true
+ },
+ {
+ "description": "zero time, in seconds",
+ "data": "PT0S",
+ "valid": true
+ },
+ {
+ "description": "zero time, in days",
+ "data": "P0D",
+ "valid": true
+ },
+ {
+ "description": "one month duration",
+ "data": "P1M",
+ "valid": true
+ },
+ {
+ "description": "one minute duration",
+ "data": "PT1M",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in hours",
+ "data": "PT36H",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in days and hours",
+ "data": "P1DT12H",
+ "valid": true
+ },
+ {
+ "description": "two weeks",
+ "data": "P2W",
+ "valid": true
+ },
+ {
+ "description": "weeks cannot be combined with other units",
+ "data": "P1Y2W",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "P২Y",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/email.json b/json/tests/draft-next/optional/format/email.json
new file mode 100644
index 0000000..5ce1c70
--- /dev/null
+++ b/json/tests/draft-next/optional/format/email.json
@@ -0,0 +1,118 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a space in the local part is valid",
+ "data": "\"joe bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a double dot in the local part is valid",
+ "data": "\"joe..bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a @ in the local part is valid",
+ "data": "\"joe@bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "an IPv4-address-literal after the @ is valid",
+ "data": "joe.bloggs@[127.0.0.1]",
+ "valid": true
+ },
+ {
+ "description": "an IPv6-address-literal after the @ is valid",
+ "data": "joe.bloggs@[IPv6:::1]",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid domain",
+ "data": "joe.bloggs@invalid=domain.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid IPv4-address-literal",
+ "data": "joe.bloggs@[127.0.0.300]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/hostname.json b/json/tests/draft-next/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft-next/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/idn-email.json b/json/tests/draft-next/optional/format/idn-email.json
new file mode 100644
index 0000000..6e21374
--- /dev/null
+++ b/json/tests/draft-next/optional/format/idn-email.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "validation of an internationalized e-mail addresses",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid idn e-mail (example@example.test in Hangul)",
+ "data": "실례@실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "an invalid idn e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/idn-hostname.json b/json/tests/draft-next/optional/format/idn-hostname.json
new file mode 100644
index 0000000..6c8f86a
--- /dev/null
+++ b/json/tests/draft-next/optional/format/idn-hostname.json
@@ -0,0 +1,304 @@
+[
+ {
+ "description": "validation of internationalized host names",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name (example.test in Hangul)",
+ "data": "실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "illegal first char U+302E Hangul single dot tone mark",
+ "data": "〮실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "contains illegal char U+302E Hangul single dot tone mark",
+ "data": "실〮례.테스트",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "invalid label, correct Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1",
+ "data": "-> $1.00 <--",
+ "valid": false
+ },
+ {
+ "description": "valid Chinese Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4",
+ "data": "xn--ihqwcrb4cv8a8dqg056pqjye",
+ "valid": true
+ },
+ {
+ "description": "invalid Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "xn--X",
+ "valid": false
+ },
+ {
+ "description": "U-label contains \"--\" in the 3rd and 4th position",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "XN--aa---o47jg78q",
+ "valid": false
+ },
+ {
+ "description": "U-label starts with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello",
+ "valid": false
+ },
+ {
+ "description": "U-label ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "hello-",
+ "valid": false
+ },
+ {
+ "description": "U-label starts and ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello-",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Spacing Combining Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0903hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Nonspacing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0300hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with an Enclosing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0488hello",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are PVALID, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u00df\u03c2\u0f0b\u3007",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are PVALID, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u06fd\u06fe",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u0640\u07fa",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start",
+ "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no preceding 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "a\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing preceding",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no following 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7a",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing following",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with surrounding 'l's",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7l",
+ "valid": true
+ },
+ {
+ "description": "Greek KERAIA not followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375S",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA not followed by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375\u03b2",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERESH not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "A\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05d0\u05f3\u05d1",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "A\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05d0\u05f4\u05d1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "def\u30fbabc",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no other characters",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Hiragana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u3041",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Katakana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u30a1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u4e08",
+ "valid": true
+ },
+ {
+ "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0660\u06f0",
+ "valid": false
+ },
+ {
+ "description": "Arabic-Indic digits not mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0628\u0660\u0628",
+ "valid": true
+ },
+ {
+ "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9",
+ "data": "\u06f00",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u094d\u200d\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1",
+ "data": "\u0915\u094d\u200c\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement",
+ "data": "\u0628\u064a\u200c\u0628\u064a",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/ipv4.json b/json/tests/draft-next/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft-next/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/ipv6.json b/json/tests/draft-next/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft-next/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/iri-reference.json b/json/tests/draft-next/optional/format/iri-reference.json
new file mode 100644
index 0000000..c6b4c22
--- /dev/null
+++ b/json/tests/draft-next/optional/format/iri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of IRI References",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative IRI Reference",
+ "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid relative IRI Reference",
+ "data": "/âππ",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI Reference",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "a valid IRI Reference",
+ "data": "âππ",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI fragment",
+ "data": "#ƒrägmênt",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI fragment",
+ "data": "#ƒräg\\mênt",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/iri.json b/json/tests/draft-next/optional/format/iri.json
new file mode 100644
index 0000000..a0d12ae
--- /dev/null
+++ b/json/tests/draft-next/optional/format/iri.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of IRIs",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag and parentheses",
+ "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with URL-encoded stuff",
+ "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI based on IPv6",
+ "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI based on IPv6",
+ "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative IRI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI though valid IRI reference",
+ "data": "âππ",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/json-pointer.json b/json/tests/draft-next/optional/format/json-pointer.json
new file mode 100644
index 0000000..a0346b5
--- /dev/null
+++ b/json/tests/draft-next/optional/format/json-pointer.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of JSON-pointers (JSON String Representation)",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid JSON-pointer",
+ "data": "/foo/bar~0/baz~1/%a",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (~ not escaped)",
+ "data": "/foo/bar~",
+ "valid": false
+ },
+ {
+ "description": "valid JSON-pointer with empty segment",
+ "data": "/foo//bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer with the last empty segment",
+ "data": "/foo/bar/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #1",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #2",
+ "data": "/foo",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #3",
+ "data": "/foo/0",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #4",
+ "data": "/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #5",
+ "data": "/a~1b",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #6",
+ "data": "/c%d",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #7",
+ "data": "/e^f",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #8",
+ "data": "/g|h",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #9",
+ "data": "/i\\j",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #10",
+ "data": "/k\"l",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #11",
+ "data": "/ ",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #12",
+ "data": "/m~0n",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer used adding to the last array position",
+ "data": "/foo/-",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (- used as object member name)",
+ "data": "/foo/-/bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (multiple escaped characters)",
+ "data": "/~1~0~0~1~1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #1",
+ "data": "/~1.1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #2",
+ "data": "/~0.1",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
+ "data": "#",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
+ "data": "#/",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
+ "data": "#a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #1",
+ "data": "/~0~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #2",
+ "data": "/~0/~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #1",
+ "data": "/~2",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #2",
+ "data": "/~-1",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (multiple characters not escaped)",
+ "data": "/~~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
+ "data": "a/a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/regex.json b/json/tests/draft-next/optional/format/regex.json
new file mode 100644
index 0000000..3449177
--- /dev/null
+++ b/json/tests/draft-next/optional/format/regex.json
@@ -0,0 +1,48 @@
+[
+ {
+ "description": "validation of regular expressions",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid regular expression",
+ "data": "([abc])+\\s+$",
+ "valid": true
+ },
+ {
+ "description": "a regular expression with unclosed parens is invalid",
+ "data": "^(abc]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/relative-json-pointer.json b/json/tests/draft-next/optional/format/relative-json-pointer.json
new file mode 100644
index 0000000..9309986
--- /dev/null
+++ b/json/tests/draft-next/optional/format/relative-json-pointer.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of Relative JSON Pointers (RJP)",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid upwards RJP",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "a valid downwards RJP",
+ "data": "0/foo/bar",
+ "valid": true
+ },
+ {
+ "description": "a valid up and then down RJP, with array index",
+ "data": "2/0/baz/1/zip",
+ "valid": true
+ },
+ {
+ "description": "a valid RJP taking the member or index name",
+ "data": "0#",
+ "valid": true
+ },
+ {
+ "description": "an invalid RJP that is a valid JSON Pointer",
+ "data": "/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "negative prefix",
+ "data": "-1/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "## is not a valid json-pointer",
+ "data": "0##",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus json-pointer",
+ "data": "01/a",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus octothorpe",
+ "data": "01#",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/time.json b/json/tests/draft-next/optional/format/time.json
new file mode 100644
index 0000000..5011d78
--- /dev/null
+++ b/json/tests/draft-next/optional/format/time.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of time strings",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid time string",
+ "data": "08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with leap second, Zulu",
+ "data": "23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, zero time-offset",
+ "data": "23:59:60+00:00",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong hour)",
+ "data": "22:59:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong minute)",
+ "data": "23:58:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, positive time-offset",
+ "data": "01:29:60+01:30",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large positive time-offset",
+ "data": "23:29:60+23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong hour)",
+ "data": "23:59:60+01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong minute)",
+ "data": "23:59:60+00:30",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, negative time-offset",
+ "data": "15:59:60-08:00",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large negative time-offset",
+ "data": "00:29:60-23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong hour)",
+ "data": "23:59:60-01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong minute)",
+ "data": "23:59:60-00:30",
+ "valid": false
+ },
+ {
+ "description": "a valid time string with second fraction",
+ "data": "23:20:50.52Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with precise second fraction",
+ "data": "08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with plus offset",
+ "data": "08:30:06+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with minus offset",
+ "data": "08:30:06-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with case-insensitive Z",
+ "data": "08:30:06z",
+ "valid": true
+ },
+ {
+ "description": "an invalid time string with invalid hour",
+ "data": "24:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid minute",
+ "data": "00:60:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid second",
+ "data": "00:00:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset hour",
+ "data": "01:02:03+24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset minute",
+ "data": "01:02:03+00:60",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time with both Z and numoffset",
+ "data": "01:02:03Z+00:30",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset indicator",
+ "data": "08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "01:01:01,1111",
+ "valid": false
+ },
+ {
+ "description": "no time offset",
+ "data": "12:00:00",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/uri-reference.json b/json/tests/draft-next/optional/format/uri-reference.json
new file mode 100644
index 0000000..7cdf228
--- /dev/null
+++ b/json/tests/draft-next/optional/format/uri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of URI References",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid relative URI Reference",
+ "data": "/abc",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI Reference",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "a valid URI Reference",
+ "data": "abc",
+ "valid": true
+ },
+ {
+ "description": "a valid URI fragment",
+ "data": "#fragment",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI fragment",
+ "data": "#frag\\ment",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/uri-template.json b/json/tests/draft-next/optional/format/uri-template.json
new file mode 100644
index 0000000..df355c5
--- /dev/null
+++ b/json/tests/draft-next/optional/format/uri-template.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "format: uri-template",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term}",
+ "valid": true
+ },
+ {
+ "description": "an invalid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term",
+ "valid": false
+ },
+ {
+ "description": "a valid uri-template without variables",
+ "data": "http://example.com/dictionary",
+ "valid": true
+ },
+ {
+ "description": "a valid relative uri-template",
+ "data": "dictionary/{term:1}/{term}",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/uri.json b/json/tests/draft-next/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft-next/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/format/uuid.json b/json/tests/draft-next/optional/format/uuid.json
new file mode 100644
index 0000000..e54cbc0
--- /dev/null
+++ b/json/tests/draft-next/optional/format/uuid.json
@@ -0,0 +1,85 @@
+[
+ {
+ "description": "uuid format",
+ "schema": {
+ "format": "uuid"
+ },
+ "tests": [
+ {
+ "description": "all upper-case",
+ "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all lower-case",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380",
+ "valid": true
+ },
+ {
+ "description": "mixed case",
+ "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all zeroes is valid",
+ "data": "00000000-0000-0000-0000-000000000000",
+ "valid": true
+ },
+ {
+ "description": "wrong length",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638",
+ "valid": false
+ },
+ {
+ "description": "missing section",
+ "data": "2eb8aa08-aa98-11ea-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "bad characters (not hex)",
+ "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "no dashes",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too few dashes",
+ "data": "2eb8aa08aa98-11ea-b4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too many dashes",
+ "data": "2eb8-aa08-aa98-11ea-b4aa73b44-1d16380",
+ "valid": false
+ },
+ {
+ "description": "dashes in the wrong spot",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380----",
+ "valid": false
+ },
+ {
+ "description": "valid version 4",
+ "data": "98d80576-482e-427f-8434-7f86890ab222",
+ "valid": true
+ },
+ {
+ "description": "valid version 5",
+ "data": "99c17cbb-656f-564a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 6",
+ "data": "99c17cbb-656f-664a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 15",
+ "data": "99c17cbb-656f-f64a-940f-1a4568f03487",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/non-bmp-regex.json b/json/tests/draft-next/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft-next/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/optional/refOfUnknownKeyword.json b/json/tests/draft-next/optional/refOfUnknownKeyword.json
new file mode 100644
index 0000000..5b150df
--- /dev/null
+++ b/json/tests/draft-next/optional/refOfUnknownKeyword.json
@@ -0,0 +1,44 @@
+[
+ {
+ "description": "reference of a root arbitrary keyword ",
+ "schema": {
+ "unknown-keyword": {"type": "integer"},
+ "properties": {
+ "bar": {"$ref": "#/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "reference of an arbitrary keyword of a sub-schema",
+ "schema": {
+ "properties": {
+ "foo": {"unknown-keyword": {"type": "integer"}},
+ "bar": {"$ref": "#/properties/foo/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/pattern.json b/json/tests/draft-next/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft-next/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/patternProperties.json b/json/tests/draft-next/patternProperties.json
new file mode 100644
index 0000000..c10ffcc
--- /dev/null
+++ b/json/tests/draft-next/patternProperties.json
@@ -0,0 +1,156 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "patternProperties with boolean schemas",
+ "schema": {
+ "patternProperties": {
+ "f.*": true,
+ "b.*": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property matching schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with a property matching both true and false is invalid",
+ "data": {"foobar":1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/prefixItems.json b/json/tests/draft-next/prefixItems.json
new file mode 100644
index 0000000..7d0571e
--- /dev/null
+++ b/json/tests/draft-next/prefixItems.json
@@ -0,0 +1,81 @@
+[
+ {
+ "description": "a schema given for prefixItems",
+ "schema": {
+ "prefixItems": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "prefixItems with boolean schemas",
+ "schema": {
+ "prefixItems": [true, false]
+ },
+ "tests": [
+ {
+ "description": "array with one item is valid",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with two items is invalid",
+ "data": [ 1, "foo" ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additional items are allowed by default",
+ "schema": {"prefixItems": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/properties.json b/json/tests/draft-next/properties.json
new file mode 100644
index 0000000..b86c181
--- /dev/null
+++ b/json/tests/draft-next/properties.json
@@ -0,0 +1,167 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with boolean schema",
+ "schema": {
+ "properties": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "no property present is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "only 'true' property present is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "only 'false' property present is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "both properties present is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/propertyNames.json b/json/tests/draft-next/propertyNames.json
new file mode 100644
index 0000000..8423690
--- /dev/null
+++ b/json/tests/draft-next/propertyNames.json
@@ -0,0 +1,78 @@
+[
+ {
+ "description": "propertyNames validation",
+ "schema": {
+ "propertyNames": {"maxLength": 3}
+ },
+ "tests": [
+ {
+ "description": "all property names valid",
+ "data": {
+ "f": {},
+ "foo": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "some property names invalid",
+ "data": {
+ "foo": {},
+ "foobar": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3, 4],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema true",
+ "schema": {"propertyNames": true},
+ "tests": [
+ {
+ "description": "object with any properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema false",
+ "schema": {"propertyNames": false},
+ "tests": [
+ {
+ "description": "object with any properties is invalid",
+ "data": {"foo": 1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/ref.json b/json/tests/draft-next/ref.json
new file mode 100644
index 0000000..bc87c9e
--- /dev/null
+++ b/json/tests/draft-next/ref.json
@@ -0,0 +1,581 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "prefixItems": [
+ {"type": "integer"},
+ {"$ref": "#/prefixItems/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "$defs": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/$defs/tilde~0field"},
+ "slash": {"$ref": "#/$defs/slash~1field"},
+ "percent": {"$ref": "#/$defs/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "$defs": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/$defs/a"},
+ "c": {"$ref": "#/$defs/b"}
+ },
+ "$ref": "#/$defs/c"
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref applies alongside sibling keywords",
+ "schema": {
+ "$defs": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid, maxItems valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems invalid",
+ "data": { "foo": [1, 2, 3] },
+ "valid": false
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/next/schema"
+ },
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/$defs/is-string"}
+ },
+ "$defs": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema true",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema false",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": false
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "$id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "$defs": {
+ "node": {
+ "$id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/$defs/foo%22bar"}
+ },
+ "$defs": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref creates new scope when adjacent to keywords",
+ "schema": {
+ "$defs": {
+ "A": {
+ "unevaluatedProperties": false
+ }
+ },
+ "properties": {
+ "prop1": {
+ "type": "string"
+ }
+ },
+ "$ref": "#/$defs/A"
+ },
+ "tests": [
+ {
+ "description": "referenced subschema doesn't see annotations from properties",
+ "data": {
+ "prop1": "match"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "$defs": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/$defs/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "do not evaluate the $ref inside the enum, definition exact match",
+ "data": { "type": "string" },
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/$defs/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "refs with relative uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-relative-uri-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "schema-relative-uri-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-relative-uri-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative refs with absolute uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-refs-absolute-uris-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id must be resolved against nearest parent, not just immediate parent",
+ "schema": {
+ "$id": "http://example.com/a.json",
+ "$defs": {
+ "x": {
+ "$id": "http://example.com/b/c.json",
+ "not": {
+ "$defs": {
+ "y": {
+ "$id": "d.json",
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "http://example.com/b/d.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number should pass",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number should fail",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/refRemote.json b/json/tests/draft-next/refRemote.json
new file mode 100644
index 0000000..b607432
--- /dev/null
+++ b/json/tests/draft-next/refRemote.json
@@ -0,0 +1,190 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "$id": "http://localhost:1234/",
+ "items": {
+ "$id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolder/"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolderInSubschema/",
+ "$defs": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "$id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name-defs.json#/$defs/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref with ref to defs",
+ "schema": {
+ "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json",
+ "$ref": "ref-and-defs.json"
+ },
+ "tests": [
+ {
+ "description": "invalid",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid",
+ "data": {
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/required.json b/json/tests/draft-next/required.json
new file mode 100644
index 0000000..abf18f3
--- /dev/null
+++ b/json/tests/draft-next/required.json
@@ -0,0 +1,105 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with empty array",
+ "schema": {
+ "properties": {
+ "foo": {}
+ },
+ "required": []
+ },
+ "tests": [
+ {
+ "description": "property not required",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/type.json b/json/tests/draft-next/type.json
new file mode 100644
index 0000000..8304647
--- /dev/null
+++ b/json/tests/draft-next/type.json
@@ -0,0 +1,474 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is an integer",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number (and an integer)",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/unevaluatedItems.json b/json/tests/draft-next/unevaluatedItems.json
new file mode 100644
index 0000000..af209b0
--- /dev/null
+++ b/json/tests/draft-next/unevaluatedItems.json
@@ -0,0 +1,633 @@
+[
+ {
+ "description": "unevaluatedItems true",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems false",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems as schema",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": { "type": "string" }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated items",
+ "data": [42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with uniform items",
+ "schema": {
+ "type": "array",
+ "items": { "type": "string" },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", "bar"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with tuple",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with items",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "items": true,
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested tuple",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "allOf": [
+ {
+ "prefixItems": [
+ true,
+ { "type": "number" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", 42],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", 42, true],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested items",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "items": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested unevaluatedItems",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "prefixItems": [
+ { "type": "string" }
+ ]
+ },
+ {
+ "unevaluatedItems": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with anyOf",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "anyOf": [
+ {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "prefixItems": [
+ true,
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when one schema matches and has no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "when one schema matches and has unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ },
+ {
+ "description": "when two schemas match and has no unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "when two schemas match and has unevaluated items",
+ "data": ["foo", "bar", "baz", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with oneOf",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "oneOf": [
+ {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "prefixItems": [
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with not",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "not": {
+ "not": {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ }
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with if/then/else",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "if": {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ "then": {
+ "prefixItems": [
+ true,
+ true,
+ { "const": "then" }
+ ]
+ },
+ "else": {
+ "prefixItems": [
+ true,
+ true,
+ true,
+ { "const": "else" }
+ ]
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when if matches and it has no unevaluated items",
+ "data": ["foo", "bar", "then"],
+ "valid": true
+ },
+ {
+ "description": "when if matches and it has unevaluated items",
+ "data": ["foo", "bar", "then", "else"],
+ "valid": false
+ },
+ {
+ "description": "when if doesn't match and it has no unevaluated items",
+ "data": ["foo", 42, 42, "else"],
+ "valid": true
+ },
+ {
+ "description": "when if doesn't match and it has unevaluated items",
+ "data": ["foo", 42, 42, "else", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with boolean schemas",
+ "schema": {
+ "type": "array",
+ "allOf": [true],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with $ref",
+ "schema": {
+ "type": "array",
+ "$ref": "#/$defs/bar",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false,
+ "$defs": {
+ "bar": {
+ "prefixItems": [
+ true,
+ { "type": "string" }
+ ]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "prefixItems": [ true ]
+ },
+ {
+ "unevaluatedItems": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": [ 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "item is evaluated in an uncle schema to unevaluatedItems",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "array",
+ "prefixItems": [
+ {
+ "type": "string"
+ }
+ ],
+ "unevaluatedItems": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "prefixItems": [
+ true,
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra items",
+ "data": {
+ "foo": [
+ "test"
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": [
+ "test",
+ "test"
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems depends on adjacent contains",
+ "schema": {
+ "prefixItems": [true],
+ "contains": {"type": "string"},
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "second item is evaluated by contains",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "contains fails, second item is not evaluated",
+ "data": [ 1, 2 ],
+ "valid": false
+ },
+ {
+ "description": "contains passes, second item is not evaluated",
+ "data": [ 1, 2, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems depends on multiple nested contains",
+ "schema": {
+ "allOf": [
+ { "contains": { "multipleOf": 2 } },
+ { "contains": { "multipleOf": 3 } }
+ ],
+ "unevaluatedItems": { "multipleOf": 5 }
+ },
+ "tests": [
+ {
+ "description": "5 not evaluated, passes unevaluatedItems",
+ "data": [ 2, 3, 4, 5, 6 ],
+ "valid": true
+ },
+ {
+ "description": "7 not evaluated, fails unevaluatedItems",
+ "data": [ 2, 3, 4, 7, 8 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems and contains interact to control item dependency relationship",
+ "schema": {
+ "if": {
+ "contains": {"const": "a"}
+ },
+ "then": {
+ "if": {
+ "contains": {"const": "b"}
+ },
+ "then": {
+ "if": {
+ "contains": {"const": "c"}
+ }
+ }
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "only a's are valid",
+ "data": [ "a", "a" ],
+ "valid": true
+ },
+ {
+ "description": "a's and b's are valid",
+ "data": [ "a", "b", "a", "b", "a" ],
+ "valid": true
+ },
+ {
+ "description": "a's, b's and c's are valid",
+ "data": [ "c", "a", "c", "c", "b", "a" ],
+ "valid": true
+ },
+ {
+ "description": "only b's are invalid",
+ "data": [ "b", "b" ],
+ "valid": false
+ },
+ {
+ "description": "only c's are invalid",
+ "data": [ "c", "c" ],
+ "valid": false
+ },
+ {
+ "description": "only b's and c's are invalid",
+ "data": [ "c", "b", "c", "b", "c" ],
+ "valid": false
+ },
+ {
+ "description": "only a's and c's are invalid",
+ "data": [ "c", "a", "c", "a", "c" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-array instances are valid",
+ "schema": {"unevaluatedItems": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/unevaluatedProperties.json b/json/tests/draft-next/unevaluatedProperties.json
new file mode 100644
index 0000000..e7865d1
--- /dev/null
+++ b/json/tests/draft-next/unevaluatedProperties.json
@@ -0,0 +1,1396 @@
+[
+ {
+ "description": "unevaluatedProperties true",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties schema",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": {
+ "type": "string",
+ "minLength": 3
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated properties",
+ "data": {
+ "foo": "fo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties false",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent patternProperties",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "additionalProperties": true,
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested patternProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "patternProperties": {
+ "^bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "additionalProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested unevaluatedProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": {
+ "type": "string",
+ "maxLength": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with anyOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ },
+ {
+ "properties": {
+ "quux": { "const": "quux" }
+ },
+ "required": ["quux"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when one matches and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when one matches and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "not-baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when two match and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when two match and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz",
+ "quux": "not-quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with oneOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "oneOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "quux": "quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with not",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "not": {
+ "not": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, then not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, else not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with dependentSchemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "dependentSchemas": {
+ "foo": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with boolean schemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [true],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with $ref",
+ "schema": {
+ "type": "object",
+ "$ref": "#/$defs/bar",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false,
+ "$defs": {
+ "bar": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, true with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, false with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ },
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property is evaluated in an uncle schema to unevaluatedProperties",
+ "comment": "see https://stackoverflow.com/questions/66936884/deeply-nested-unevaluatedproperties-and-their-expectations",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "object",
+ "properties": {
+ "bar": {
+ "type": "string"
+ }
+ },
+ "unevaluatedProperties": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "properties": {
+ "faz": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra properties",
+ "data": {
+ "foo": {
+ "bar": "test"
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": {
+ "bar": "test",
+ "faz": "test"
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, allOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, anyOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + single cyclic ref",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "x": { "$ref": "#" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "Single is valid",
+ "data": { "x": {} },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 1st level is invalid",
+ "data": { "x": {}, "y": {} },
+ "valid": false
+ },
+ {
+ "description": "Nested is valid",
+ "data": { "x": { "x": {} } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 2nd level is invalid",
+ "data": { "x": { "x": {}, "y": {} } },
+ "valid": false
+ },
+ {
+ "description": "Deep nested is valid",
+ "data": { "x": { "x": { "x": {} } } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 3rd level is invalid",
+ "data": { "x": { "x": { "x": {}, "y": {} } } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + ref inside allOf / oneOf",
+ "schema": {
+ "$defs": {
+ "one": {
+ "properties": { "a": true }
+ },
+ "two": {
+ "required": ["x"],
+ "properties": { "x": true }
+ }
+ },
+ "allOf": [
+ { "$ref": "#/$defs/one" },
+ { "properties": { "b": true } },
+ {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ {
+ "required": ["y"],
+ "properties": { "y": true }
+ }
+ ]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid (no x or y)",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a and b are invalid (no x or y)",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "x and y are invalid",
+ "data": { "x": 1, "y": 1 },
+ "valid": false
+ },
+ {
+ "description": "a and x are valid",
+ "data": { "a": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and y are valid",
+ "data": { "a": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x are valid",
+ "data": { "a": 1, "b": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and y are valid",
+ "data": { "a": 1, "b": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x and y are invalid",
+ "data": { "a": 1, "b": 1, "x": 1, "y": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dynamic evalation inside nested refs",
+ "schema": {
+ "$defs": {
+ "one": {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ { "required": ["b"], "properties": { "b": true } },
+ { "required": ["xx"], "patternProperties": { "x": true } },
+ { "required": ["all"], "unevaluatedProperties": true }
+ ]
+ },
+ "two": {
+ "oneOf": [
+ { "required": ["c"], "properties": { "c": true } },
+ { "required": ["d"], "properties": { "d": true } }
+ ]
+ }
+ },
+ "oneOf": [
+ { "$ref": "#/$defs/one" },
+ { "required": ["a"], "properties": { "a": true } }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a is valid",
+ "data": { "a": 1 },
+ "valid": true
+ },
+ {
+ "description": "b is valid",
+ "data": { "b": 1 },
+ "valid": true
+ },
+ {
+ "description": "c is valid",
+ "data": { "c": 1 },
+ "valid": true
+ },
+ {
+ "description": "d is valid",
+ "data": { "d": 1 },
+ "valid": true
+ },
+ {
+ "description": "a + b is invalid",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + c is invalid",
+ "data": { "a": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + d is invalid",
+ "data": { "a": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + c is invalid",
+ "data": { "b": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + d is invalid",
+ "data": { "b": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "c + d is invalid",
+ "data": { "c": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx is valid",
+ "data": { "xx": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foox is valid",
+ "data": { "xx": 1, "foox": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foo is invalid",
+ "data": { "xx": 1, "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + a is invalid",
+ "data": { "xx": 1, "a": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + b is invalid",
+ "data": { "xx": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + c is invalid",
+ "data": { "xx": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + d is invalid",
+ "data": { "xx": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "all is valid",
+ "data": { "all": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + foo is valid",
+ "data": { "all": 1, "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + a is invalid",
+ "data": { "all": 1, "a": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties depends on adjacent contains",
+ "schema": {
+ "properties": {
+ "foo": { "type": "number" }
+ },
+ "contains": { "type": "string" },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "bar is evaluated by contains",
+ "data": { "foo": 1, "bar": "foo" },
+ "valid": true
+ },
+ {
+ "description": "contains fails, bar is not evaluated",
+ "data": { "foo": 1, "bar": 2 },
+ "valid": false
+ },
+ {
+ "description": "contains passes, bar is not evaluated",
+ "data": { "foo": 1, "bar": 2, "baz": "foo" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties depends on multiple nested contains",
+ "schema": {
+ "allOf": [
+ { "contains": { "multipleOf": 2 } },
+ { "contains": { "multipleOf": 3 } }
+ ],
+ "unevaluatedProperties": { "multipleOf": 5 }
+ },
+ "tests": [
+ {
+ "description": "5 not evaluated, passes unevaluatedItems",
+ "data": { "a": 2, "b": 3, "c": 4, "d": 5, "e": 6 },
+ "valid": true
+ },
+ {
+ "description": "7 not evaluated, fails unevaluatedItems",
+ "data": { "a": 2, "b": 3, "c": 4, "d": 7, "e": 8 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-object instances are valid",
+ "schema": {"unevaluatedProperties": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/uniqueItems.json b/json/tests/draft-next/uniqueItems.json
new file mode 100644
index 0000000..85c619d
--- /dev/null
+++ b/json/tests/draft-next/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/unknownKeyword.json b/json/tests/draft-next/unknownKeyword.json
new file mode 100644
index 0000000..e46657d
--- /dev/null
+++ b/json/tests/draft-next/unknownKeyword.json
@@ -0,0 +1,56 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "$defs": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_unknown0" },
+ { "$ref": "#/$defs/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft-next/vocabulary.json b/json/tests/draft-next/vocabulary.json
new file mode 100644
index 0000000..65b81ea
--- /dev/null
+++ b/json/tests/draft-next/vocabulary.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "schema that uses custom metaschema with with no validation vocabulary",
+ "schema": {
+ "$id": "https://schema/using/no/validation",
+ "$schema": "http://localhost:1234/draft-next/metaschema-no-validation.json",
+ "properties": {
+ "badProperty": false,
+ "numberProperty": {
+ "minimum": 10
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "applicator vocabulary still works",
+ "data": {
+ "badProperty": "this property should not exist"
+ },
+ "valid": false
+ },
+ {
+ "description": "no validation: valid number",
+ "data": {
+ "numberProperty": 20
+ },
+ "valid": true
+ },
+ {
+ "description": "no validation: invalid number, but it still validates",
+ "data": {
+ "numberProperty": 1
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/additionalItems.json b/json/tests/draft2019-09/additionalItems.json
new file mode 100644
index 0000000..784bc84
--- /dev/null
+++ b/json/tests/draft2019-09/additionalItems.json
@@ -0,0 +1,149 @@
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [{}],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ null, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ null, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "when items is schema, additionalItems does nothing",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems permitted",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" } ] }
+ ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, null ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, invalid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" }, { "type": "string" } ] }
+ ],
+ "items": [ {"type": "integer" } ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, "hello" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items validation adjusts the starting index for additionalItems",
+ "schema": {
+ "items": [ { "type": "string" } ],
+ "additionalItems": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/additionalProperties.json b/json/tests/draft2019-09/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft2019-09/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/allOf.json b/json/tests/draft2019-09/allOf.json
new file mode 100644
index 0000000..ec9319e
--- /dev/null
+++ b/json/tests/draft2019-09/allOf.json
@@ -0,0 +1,294 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all true",
+ "schema": {"allOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, some false",
+ "schema": {"allOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all false",
+ "schema": {"allOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/anchor.json b/json/tests/draft2019-09/anchor.json
new file mode 100644
index 0000000..416c224
--- /dev/null
+++ b/json/tests/draft2019-09/anchor.json
@@ -0,0 +1,173 @@
+[
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with absolute URI",
+ "schema": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar",
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$anchor inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $anchor buried in the enum",
+ "schema": {
+ "$defs": {
+ "anchor_in_enum": {
+ "enum": [
+ {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ ]
+ },
+ "real_identifier_in_schema": {
+ "$anchor": "my_anchor",
+ "type": "string"
+ },
+ "zzz_anchor_in_const": {
+ "const": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/anchor_in_enum" },
+ { "$ref": "#my_anchor" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "in implementations that strip $anchor, this may match either $def",
+ "data": {
+ "type": "null"
+ },
+ "valid": false
+ },
+ {
+ "description": "match $ref to $anchor",
+ "data": "a string to match #/$defs/anchor_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $anchor",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "same $anchor with different base uri",
+ "schema": {
+ "$id": "http://localhost:1234/foobar",
+ "$defs": {
+ "A": {
+ "$id": "child1",
+ "allOf": [
+ {
+ "$id": "child2",
+ "$anchor": "my_anchor",
+ "type": "number"
+ },
+ {
+ "$anchor": "my_anchor",
+ "type": "string"
+ }
+ ]
+ }
+ },
+ "$ref": "child1#my_anchor"
+ },
+ "tests": [
+ {
+ "description": "$ref should resolve to /$defs/A/allOf/1",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "$ref should not resolve to /$defs/A/allOf/0",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/anyOf.json b/json/tests/draft2019-09/anyOf.json
new file mode 100644
index 0000000..ab5eb38
--- /dev/null
+++ b/json/tests/draft2019-09/anyOf.json
@@ -0,0 +1,189 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all true",
+ "schema": {"anyOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, some true",
+ "schema": {"anyOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all false",
+ "schema": {"anyOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/boolean_schema.json b/json/tests/draft2019-09/boolean_schema.json
new file mode 100644
index 0000000..6d40f23
--- /dev/null
+++ b/json/tests/draft2019-09/boolean_schema.json
@@ -0,0 +1,104 @@
+[
+ {
+ "description": "boolean schema 'true'",
+ "schema": true,
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "boolean true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "boolean false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean schema 'false'",
+ "schema": false,
+ "tests": [
+ {
+ "description": "number is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "boolean true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "boolean false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/const.json b/json/tests/draft2019-09/const.json
new file mode 100644
index 0000000..1c2cafc
--- /dev/null
+++ b/json/tests/draft2019-09/const.json
@@ -0,0 +1,342 @@
+[
+ {
+ "description": "const validation",
+ "schema": {"const": 2},
+ "tests": [
+ {
+ "description": "same value is valid",
+ "data": 2,
+ "valid": true
+ },
+ {
+ "description": "another value is invalid",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with object",
+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
+ "tests": [
+ {
+ "description": "same object is valid",
+ "data": {"foo": "bar", "baz": "bax"},
+ "valid": true
+ },
+ {
+ "description": "same object with different property order is valid",
+ "data": {"baz": "bax", "foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "another object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": [1, 2],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with array",
+ "schema": {"const": [{ "foo": "bar" }]},
+ "tests": [
+ {
+ "description": "same array is valid",
+ "data": [{"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "another array item is invalid",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "array with additional items is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with null",
+ "schema": {"const": null},
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "not null is invalid",
+ "data": 0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with false does not match 0",
+ "schema": {"const": false},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with true does not match 1",
+ "schema": {"const": true},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [false] does not match [0]",
+ "schema": {"const": [false]},
+ "tests": [
+ {
+ "description": "[false] is valid",
+ "data": [false],
+ "valid": true
+ },
+ {
+ "description": "[0] is invalid",
+ "data": [0],
+ "valid": false
+ },
+ {
+ "description": "[0.0] is invalid",
+ "data": [0.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [true] does not match [1]",
+ "schema": {"const": [true]},
+ "tests": [
+ {
+ "description": "[true] is valid",
+ "data": [true],
+ "valid": true
+ },
+ {
+ "description": "[1] is invalid",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "[1.0] is invalid",
+ "data": [1.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": false} does not match {\"a\": 0}",
+ "schema": {"const": {"a": false}},
+ "tests": [
+ {
+ "description": "{\"a\": false} is valid",
+ "data": {"a": false},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 0} is invalid",
+ "data": {"a": 0},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 0.0} is invalid",
+ "data": {"a": 0.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": true} does not match {\"a\": 1}",
+ "schema": {"const": {"a": true}},
+ "tests": [
+ {
+ "description": "{\"a\": true} is valid",
+ "data": {"a": true},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 1} is invalid",
+ "data": {"a": 1},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 1.0} is invalid",
+ "data": {"a": 1.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 0 does not match other zero-like types",
+ "schema": {"const": 0},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "empty string is invalid",
+ "data": "",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 1 does not match true",
+ "schema": {"const": 1},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "const with -2.0 matches integer and float types",
+ "schema": {"const": -2.0},
+ "tests": [
+ {
+ "description": "integer -2 is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "integer 2 is invalid",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "float -2.0 is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float 2.0 is invalid",
+ "data": 2.0,
+ "valid": false
+ },
+ {
+ "description": "float -2.00001 is invalid",
+ "data": -2.00001,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "float and integers are equal up to 64-bit representation limits",
+ "schema": {"const": 9007199254740992},
+ "tests": [
+ {
+ "description": "integer is valid",
+ "data": 9007199254740992,
+ "valid": true
+ },
+ {
+ "description": "integer minus one is invalid",
+ "data": 9007199254740991,
+ "valid": false
+ },
+ {
+ "description": "float is valid",
+ "data": 9007199254740992.0,
+ "valid": true
+ },
+ {
+ "description": "float minus one is invalid",
+ "data": 9007199254740991.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "const": "hello\u0000there" },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/contains.json b/json/tests/draft2019-09/contains.json
new file mode 100644
index 0000000..215da98
--- /dev/null
+++ b/json/tests/draft2019-09/contains.json
@@ -0,0 +1,150 @@
+[
+ {
+ "description": "contains keyword validation",
+ "schema": {
+ "contains": {"minimum": 5}
+ },
+ "tests": [
+ {
+ "description": "array with item matching schema (5) is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with item matching schema (6) is valid",
+ "data": [3, 4, 6],
+ "valid": true
+ },
+ {
+ "description": "array with two items matching schema (5, 6) is valid",
+ "data": [3, 4, 5, 6],
+ "valid": true
+ },
+ {
+ "description": "array without items matching schema is invalid",
+ "data": [2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "not array is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with const keyword",
+ "schema": {
+ "contains": { "const": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item 5 is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with two items 5 is valid",
+ "data": [3, 4, 5, 5],
+ "valid": true
+ },
+ {
+ "description": "array without item 5 is invalid",
+ "data": [1, 2, 3, 4],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema true",
+ "schema": {"contains": true},
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema false",
+ "schema": {"contains": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "non-arrays are valid",
+ "data": "contains does not apply to strings",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items + contains",
+ "schema": {
+ "items": { "multipleOf": 2 },
+ "contains": { "multipleOf": 3 }
+ },
+ "tests": [
+ {
+ "description": "matches items, does not match contains",
+ "data": [ 2, 4, 8 ],
+ "valid": false
+ },
+ {
+ "description": "does not match items, matches contains",
+ "data": [ 3, 6, 9 ],
+ "valid": false
+ },
+ {
+ "description": "matches both items and contains",
+ "data": [ 6, 12 ],
+ "valid": true
+ },
+ {
+ "description": "matches neither items nor contains",
+ "data": [ 1, 5 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains with false if subschema",
+ "schema": {
+ "contains": {
+ "if": false,
+ "else": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/content.json b/json/tests/draft2019-09/content.json
new file mode 100644
index 0000000..44688e8
--- /dev/null
+++ b/json/tests/draft2019-09/content.json
@@ -0,0 +1,127 @@
+[
+ {
+ "description": "validation of string-encoded content based on media type",
+ "schema": {
+ "contentMediaType": "application/json"
+ },
+ "tests": [
+ {
+ "description": "a valid JSON document",
+ "data": "{\"foo\": \"bar\"}",
+ "valid": true
+ },
+ {
+ "description": "an invalid JSON document; validates true",
+ "data": "{:}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary string-encoding",
+ "schema": {
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64 string",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string (% is not a valid character); validates true",
+ "data": "eyJmb28iOi%iYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents with schema",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64",
+ "contentSchema": { "required": ["foo"], "properties": { "foo": { "type": "string" } } }
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "another valid base64-encoded JSON document",
+ "data": "eyJib28iOiAyMCwgImZvbyI6ICJiYXoifQ==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64-encoded JSON document; validates true",
+ "data": "eyJib28iOiAyMH0=",
+ "valid": true
+ },
+ {
+ "description": "an empty object as a base64-encoded JSON document; validates true",
+ "data": "e30=",
+ "valid": true
+ },
+ {
+ "description": "an empty array as a base64-encoded JSON document",
+ "data": "W10=",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/default.json b/json/tests/draft2019-09/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft2019-09/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/defs.json b/json/tests/draft2019-09/defs.json
new file mode 100644
index 0000000..70e9dc0
--- /dev/null
+++ b/json/tests/draft2019-09/defs.json
@@ -0,0 +1,18 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"},
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {"$defs": {"foo": {"type": "integer"}}},
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {"$defs": {"foo": {"type": 1}}},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/dependentRequired.json b/json/tests/draft2019-09/dependentRequired.json
new file mode 100644
index 0000000..c817120
--- /dev/null
+++ b/json/tests/draft2019-09/dependentRequired.json
@@ -0,0 +1,142 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {"dependentRequired": {"bar": ["foo"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "empty dependents",
+ "schema": {"dependentRequired": {"bar": []}},
+ "tests": [
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "object with one property",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "non-object is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependents required",
+ "schema": {"dependentRequired": {"quux": ["foo", "bar"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentRequired": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "CRLF",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quotes",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "CRLF missing dependent",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quotes missing dependent",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/dependentSchemas.json b/json/tests/draft2019-09/dependentSchemas.json
new file mode 100644
index 0000000..2ba1a75
--- /dev/null
+++ b/json/tests/draft2019-09/dependentSchemas.json
@@ -0,0 +1,129 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {
+ "dependentSchemas": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean subschemas",
+ "schema": {
+ "dependentSchemas": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property having schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property having schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentSchemas": {
+ "foo\tbar": {"minProperties": 4},
+ "foo'bar": {"required": ["foo\"bar"]}
+ }
+ },
+ "tests": [
+ {
+ "description": "quoted tab",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quote",
+ "data": {
+ "foo'bar": {"foo\"bar": 1}
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted tab invalid under dependent schema",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quote invalid under dependent schema",
+ "data": {"foo'bar": 1},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/enum.json b/json/tests/draft2019-09/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft2019-09/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/exclusiveMaximum.json b/json/tests/draft2019-09/exclusiveMaximum.json
new file mode 100644
index 0000000..dc3cd70
--- /dev/null
+++ b/json/tests/draft2019-09/exclusiveMaximum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "exclusiveMaximum": 3.0
+ },
+ "tests": [
+ {
+ "description": "below the exclusiveMaximum is valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ },
+ {
+ "description": "above the exclusiveMaximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/exclusiveMinimum.json b/json/tests/draft2019-09/exclusiveMinimum.json
new file mode 100644
index 0000000..b38d7ec
--- /dev/null
+++ b/json/tests/draft2019-09/exclusiveMinimum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "exclusiveMinimum": 1.1
+ },
+ "tests": [
+ {
+ "description": "above the exclusiveMinimum is valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "below the exclusiveMinimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/format.json b/json/tests/draft2019-09/format.json
new file mode 100644
index 0000000..a4b51d2
--- /dev/null
+++ b/json/tests/draft2019-09/format.json
@@ -0,0 +1,686 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-email format",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "regex format",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-hostname format",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date format",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "time format",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "json-pointer format",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative-json-pointer format",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri format",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri-reference format",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-reference format",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-template format",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uuid format",
+ "schema": { "format": "uuid" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "duration format",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/id.json b/json/tests/draft2019-09/id.json
new file mode 100644
index 0000000..e225aad
--- /dev/null
+++ b/json/tests/draft2019-09/id.json
@@ -0,0 +1,256 @@
+[
+ {
+ "description": "Invalid use of fragments in location-independent $id",
+ "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"},
+ "tests": [
+ {
+ "description": "Identifier name",
+ "data": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name and no ref",
+ "data": {
+ "$defs": {
+ "A": { "$id": "#foo" }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path",
+ "data": {
+ "$ref": "#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Valid use of empty fragments in location-independent $id",
+ "comment": "These are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2019-09/schema"
+ },
+ "tests": [
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/$defs/B",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Unnormalized $ids are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2019-09/schema"
+ },
+ "tests": [
+ {
+ "description": "Unnormalized identifier",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $id buried in the enum",
+ "schema": {
+ "$defs": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_enum" },
+ { "$ref": "https://localhost:1234/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to $id",
+ "data": "a string to match #/$defs/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/if-then-else.json b/json/tests/draft2019-09/if-then-else.json
new file mode 100644
index 0000000..284e919
--- /dev/null
+++ b/json/tests/draft2019-09/if-then-else.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "ignore if without then or else",
+ "schema": {
+ "if": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone if",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone if",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore then without if",
+ "schema": {
+ "then": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone then",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone then",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore else without if",
+ "schema": {
+ "else": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone else",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone else",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and then without else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid when if test fails",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and else without then",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when if test passes",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validate against correct branch, then vs else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-interference across combined schemas",
+ "schema": {
+ "allOf": [
+ {
+ "if": {
+ "exclusiveMaximum": 0
+ }
+ },
+ {
+ "then": {
+ "minimum": -10
+ }
+ },
+ {
+ "else": {
+ "multipleOf": 2
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid, but would have been invalid through then",
+ "data": -100,
+ "valid": true
+ },
+ {
+ "description": "valid, but would have been invalid through else",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema true",
+ "schema": {
+ "if": true,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema true in if always chooses the then path (valid)",
+ "data": "then",
+ "valid": true
+ },
+ {
+ "description": "boolean schema true in if always chooses the then path (invalid)",
+ "data": "else",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema false",
+ "schema": {
+ "if": false,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema false in if always chooses the else path (invalid)",
+ "data": "then",
+ "valid": false
+ },
+ {
+ "description": "boolean schema false in if always chooses the else path (valid)",
+ "data": "else",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if appears at the end when serialized (keyword processing sequence)",
+ "schema": {
+ "then": { "const": "yes" },
+ "else": { "const": "other" },
+ "if": { "maxLength": 4 }
+ },
+ "tests": [
+ {
+ "description": "yes redirects to then and passes",
+ "data": "yes",
+ "valid": true
+ },
+ {
+ "description": "other redirects to else and passes",
+ "data": "other",
+ "valid": true
+ },
+ {
+ "description": "no redirects to then and fails",
+ "data": "no",
+ "valid": false
+ },
+ {
+ "description": "invalid redirects to else and fails",
+ "data": "invalid",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/infinite-loop-detection.json b/json/tests/draft2019-09/infinite-loop-detection.json
new file mode 100644
index 0000000..9c3c362
--- /dev/null
+++ b/json/tests/draft2019-09/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "$defs": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/items.json b/json/tests/draft2019-09/items.json
new file mode 100644
index 0000000..6e98ee8
--- /dev/null
+++ b/json/tests/draft2019-09/items.json
@@ -0,0 +1,250 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (true)",
+ "schema": {"items": true},
+ "tests": [
+ {
+ "description": "any array is valid",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (false)",
+ "schema": {"items": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": [ 1, "foo", true ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schemas",
+ "schema": {
+ "items": [true, false]
+ },
+ "tests": [
+ {
+ "description": "array with one item is valid",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with two items is invalid",
+ "data": [ 1, "foo" ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "$defs": {
+ "item": {
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/$defs/sub-item" },
+ { "$ref": "#/$defs/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/maxContains.json b/json/tests/draft2019-09/maxContains.json
new file mode 100644
index 0000000..3c42fb3
--- /dev/null
+++ b/json/tests/draft2019-09/maxContains.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "maxContains without contains is ignored",
+ "schema": {
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "two items still valid against lone maxContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [ 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "some elements match, valid maxContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, invalid maxContains",
+ "data": [ 1, 2, 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains < maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 1,
+ "maxContains": 3
+ },
+ "tests": [
+ {
+ "description": "actual < minContains < maxContains",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "minContains < actual < maxContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "minContains < maxContains < actual",
+ "data": [ 1, 1, 1, 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/maxItems.json b/json/tests/draft2019-09/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft2019-09/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/maxLength.json b/json/tests/draft2019-09/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft2019-09/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/maxProperties.json b/json/tests/draft2019-09/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft2019-09/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/maximum.json b/json/tests/draft2019-09/maximum.json
new file mode 100644
index 0000000..6844a39
--- /dev/null
+++ b/json/tests/draft2019-09/maximum.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/minContains.json b/json/tests/draft2019-09/minContains.json
new file mode 100644
index 0000000..163247f
--- /dev/null
+++ b/json/tests/draft2019-09/minContains.json
@@ -0,0 +1,197 @@
+[
+ {
+ "description": "minContains without contains is ignored",
+ "schema": {
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone minContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "zero items still valid against lone minContains",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=1 with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "no elements match",
+ "data": [ 2 ],
+ "valid": false
+ },
+ {
+ "description": "single element matches, valid minContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=2 with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "some elements match, invalid minContains",
+ "data": [ 1, 2 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid minContains (exactly as needed)",
+ "data": [ 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains (more than needed)",
+ "data": [ 1, 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [ 1, 2, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains = minContains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 2,
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [ 1, 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains and minContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains < minContains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 1,
+ "minContains": 3
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains",
+ "data": [ 1, 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains and minContains",
+ "data": [ 1, 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0 with no maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 0
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "minContains = 0 makes contains always pass",
+ "data": [ 2 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0 with maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 0,
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "not more than maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "too many",
+ "data": [ 1, 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/minItems.json b/json/tests/draft2019-09/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft2019-09/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/minLength.json b/json/tests/draft2019-09/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft2019-09/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/minProperties.json b/json/tests/draft2019-09/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft2019-09/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/minimum.json b/json/tests/draft2019-09/minimum.json
new file mode 100644
index 0000000..21ae50e
--- /dev/null
+++ b/json/tests/draft2019-09/minimum.json
@@ -0,0 +1,69 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/multipleOf.json b/json/tests/draft2019-09/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft2019-09/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/not.json b/json/tests/draft2019-09/not.json
new file mode 100644
index 0000000..98de0ed
--- /dev/null
+++ b/json/tests/draft2019-09/not.json
@@ -0,0 +1,117 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema true",
+ "schema": {"not": true},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema false",
+ "schema": {"not": false},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/oneOf.json b/json/tests/draft2019-09/oneOf.json
new file mode 100644
index 0000000..eeb7ae8
--- /dev/null
+++ b/json/tests/draft2019-09/oneOf.json
@@ -0,0 +1,274 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all true",
+ "schema": {"oneOf": [true, true, true]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, one true",
+ "schema": {"oneOf": [true, false, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, more than one true",
+ "schema": {"oneOf": [true, true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all false",
+ "schema": {"oneOf": [false, false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": true,
+ "baz": true
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": true
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/bignum.json b/json/tests/draft2019-09/optional/bignum.json
new file mode 100644
index 0000000..3f49226
--- /dev/null
+++ b/json/tests/draft2019-09/optional/bignum.json
@@ -0,0 +1,93 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "exclusiveMaximum": 972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "exclusiveMinimum": -972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/ecmascript-regex.json b/json/tests/draft2019-09/optional/ecmascript-regex.json
new file mode 100644
index 0000000..1beb0b3
--- /dev/null
+++ b/json/tests/draft2019-09/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/float-overflow.json b/json/tests/draft2019-09/optional/float-overflow.json
new file mode 100644
index 0000000..52ff982
--- /dev/null
+++ b/json/tests/draft2019-09/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "integer", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/date-time.json b/json/tests/draft2019-09/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/date.json b/json/tests/draft2019-09/optional/format/date.json
new file mode 100644
index 0000000..b4f61ee
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/date.json
@@ -0,0 +1,223 @@
+[
+ {
+ "description": "validation of date strings",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date string",
+ "data": "1963-06-19",
+ "valid": true
+ },
+ {
+ "description": "a valid date string with 31 days in January",
+ "data": "2020-01-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in January",
+ "data": "2020-01-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 28 days in February (normal)",
+ "data": "2021-02-28",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 29 days in February (normal)",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 29 days in February (leap)",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 30 days in February (leap)",
+ "data": "2020-02-30",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in March",
+ "data": "2020-03-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in March",
+ "data": "2020-03-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in April",
+ "data": "2020-04-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in April",
+ "data": "2020-04-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in May",
+ "data": "2020-05-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in May",
+ "data": "2020-05-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in June",
+ "data": "2020-06-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in June",
+ "data": "2020-06-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in July",
+ "data": "2020-07-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in July",
+ "data": "2020-07-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in August",
+ "data": "2020-08-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in August",
+ "data": "2020-08-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in September",
+ "data": "2020-09-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in September",
+ "data": "2020-09-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in October",
+ "data": "2020-10-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in October",
+ "data": "2020-10-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in November",
+ "data": "2020-11-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in November",
+ "data": "2020-11-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in December",
+ "data": "2020-12-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in December",
+ "data": "2020-12-32",
+ "valid": false
+ },
+ {
+ "description": "a invalid date string with invalid month",
+ "data": "2020-13-01",
+ "valid": false
+ },
+ {
+ "description": "an invalid date string",
+ "data": "06/19/1963",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350",
+ "valid": false
+ },
+ {
+ "description": "non-padded month dates are not valid",
+ "data": "1998-1-20",
+ "valid": false
+ },
+ {
+ "description": "non-padded day dates are not valid",
+ "data": "1998-01-1",
+ "valid": false
+ },
+ {
+ "description": "invalid month",
+ "data": "1998-13-01",
+ "valid": false
+ },
+ {
+ "description": "invalid month-day combination",
+ "data": "1998-04-31",
+ "valid": false
+ },
+ {
+ "description": "2021 is not a leap year",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "2020 is a leap year",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1963-06-1৪",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/duration.json b/json/tests/draft2019-09/optional/format/duration.json
new file mode 100644
index 0000000..741348a
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/duration.json
@@ -0,0 +1,128 @@
+[
+ {
+ "description": "validation of duration strings",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid duration string",
+ "data": "P4DT12H30M5S",
+ "valid": true
+ },
+ {
+ "description": "an invalid duration string",
+ "data": "PT1D",
+ "valid": false
+ },
+ {
+ "description": "no elements present",
+ "data": "P",
+ "valid": false
+ },
+ {
+ "description": "no time elements present",
+ "data": "P1YT",
+ "valid": false
+ },
+ {
+ "description": "no date or time elements present",
+ "data": "PT",
+ "valid": false
+ },
+ {
+ "description": "elements out of order",
+ "data": "P2D1Y",
+ "valid": false
+ },
+ {
+ "description": "missing time separator",
+ "data": "P1D2H",
+ "valid": false
+ },
+ {
+ "description": "time element in the date position",
+ "data": "P2S",
+ "valid": false
+ },
+ {
+ "description": "four years duration",
+ "data": "P4Y",
+ "valid": true
+ },
+ {
+ "description": "zero time, in seconds",
+ "data": "PT0S",
+ "valid": true
+ },
+ {
+ "description": "zero time, in days",
+ "data": "P0D",
+ "valid": true
+ },
+ {
+ "description": "one month duration",
+ "data": "P1M",
+ "valid": true
+ },
+ {
+ "description": "one minute duration",
+ "data": "PT1M",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in hours",
+ "data": "PT36H",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in days and hours",
+ "data": "P1DT12H",
+ "valid": true
+ },
+ {
+ "description": "two weeks",
+ "data": "P2W",
+ "valid": true
+ },
+ {
+ "description": "weeks cannot be combined with other units",
+ "data": "P1Y2W",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "P২Y",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/email.json b/json/tests/draft2019-09/optional/format/email.json
new file mode 100644
index 0000000..d6761a4
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/email.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/hostname.json b/json/tests/draft2019-09/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/idn-email.json b/json/tests/draft2019-09/optional/format/idn-email.json
new file mode 100644
index 0000000..6e21374
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/idn-email.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "validation of an internationalized e-mail addresses",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid idn e-mail (example@example.test in Hangul)",
+ "data": "실례@실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "an invalid idn e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/idn-hostname.json b/json/tests/draft2019-09/optional/format/idn-hostname.json
new file mode 100644
index 0000000..6c8f86a
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/idn-hostname.json
@@ -0,0 +1,304 @@
+[
+ {
+ "description": "validation of internationalized host names",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name (example.test in Hangul)",
+ "data": "실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "illegal first char U+302E Hangul single dot tone mark",
+ "data": "〮실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "contains illegal char U+302E Hangul single dot tone mark",
+ "data": "실〮례.테스트",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "invalid label, correct Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1",
+ "data": "-> $1.00 <--",
+ "valid": false
+ },
+ {
+ "description": "valid Chinese Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4",
+ "data": "xn--ihqwcrb4cv8a8dqg056pqjye",
+ "valid": true
+ },
+ {
+ "description": "invalid Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "xn--X",
+ "valid": false
+ },
+ {
+ "description": "U-label contains \"--\" in the 3rd and 4th position",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "XN--aa---o47jg78q",
+ "valid": false
+ },
+ {
+ "description": "U-label starts with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello",
+ "valid": false
+ },
+ {
+ "description": "U-label ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "hello-",
+ "valid": false
+ },
+ {
+ "description": "U-label starts and ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello-",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Spacing Combining Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0903hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Nonspacing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0300hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with an Enclosing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0488hello",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are PVALID, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u00df\u03c2\u0f0b\u3007",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are PVALID, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u06fd\u06fe",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u0640\u07fa",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start",
+ "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no preceding 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "a\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing preceding",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no following 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7a",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing following",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with surrounding 'l's",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7l",
+ "valid": true
+ },
+ {
+ "description": "Greek KERAIA not followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375S",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA not followed by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375\u03b2",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERESH not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "A\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05d0\u05f3\u05d1",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "A\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05d0\u05f4\u05d1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "def\u30fbabc",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no other characters",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Hiragana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u3041",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Katakana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u30a1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u4e08",
+ "valid": true
+ },
+ {
+ "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0660\u06f0",
+ "valid": false
+ },
+ {
+ "description": "Arabic-Indic digits not mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0628\u0660\u0628",
+ "valid": true
+ },
+ {
+ "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9",
+ "data": "\u06f00",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u094d\u200d\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1",
+ "data": "\u0915\u094d\u200c\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement",
+ "data": "\u0628\u064a\u200c\u0628\u064a",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/ipv4.json b/json/tests/draft2019-09/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/ipv6.json b/json/tests/draft2019-09/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/iri-reference.json b/json/tests/draft2019-09/optional/format/iri-reference.json
new file mode 100644
index 0000000..c6b4c22
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/iri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of IRI References",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative IRI Reference",
+ "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid relative IRI Reference",
+ "data": "/âππ",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI Reference",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "a valid IRI Reference",
+ "data": "âππ",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI fragment",
+ "data": "#ƒrägmênt",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI fragment",
+ "data": "#ƒräg\\mênt",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/iri.json b/json/tests/draft2019-09/optional/format/iri.json
new file mode 100644
index 0000000..a0d12ae
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/iri.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of IRIs",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag and parentheses",
+ "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with URL-encoded stuff",
+ "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI based on IPv6",
+ "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI based on IPv6",
+ "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative IRI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI though valid IRI reference",
+ "data": "âππ",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/json-pointer.json b/json/tests/draft2019-09/optional/format/json-pointer.json
new file mode 100644
index 0000000..a0346b5
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/json-pointer.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of JSON-pointers (JSON String Representation)",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid JSON-pointer",
+ "data": "/foo/bar~0/baz~1/%a",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (~ not escaped)",
+ "data": "/foo/bar~",
+ "valid": false
+ },
+ {
+ "description": "valid JSON-pointer with empty segment",
+ "data": "/foo//bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer with the last empty segment",
+ "data": "/foo/bar/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #1",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #2",
+ "data": "/foo",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #3",
+ "data": "/foo/0",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #4",
+ "data": "/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #5",
+ "data": "/a~1b",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #6",
+ "data": "/c%d",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #7",
+ "data": "/e^f",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #8",
+ "data": "/g|h",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #9",
+ "data": "/i\\j",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #10",
+ "data": "/k\"l",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #11",
+ "data": "/ ",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #12",
+ "data": "/m~0n",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer used adding to the last array position",
+ "data": "/foo/-",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (- used as object member name)",
+ "data": "/foo/-/bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (multiple escaped characters)",
+ "data": "/~1~0~0~1~1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #1",
+ "data": "/~1.1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #2",
+ "data": "/~0.1",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
+ "data": "#",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
+ "data": "#/",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
+ "data": "#a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #1",
+ "data": "/~0~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #2",
+ "data": "/~0/~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #1",
+ "data": "/~2",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #2",
+ "data": "/~-1",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (multiple characters not escaped)",
+ "data": "/~~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
+ "data": "a/a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/regex.json b/json/tests/draft2019-09/optional/format/regex.json
new file mode 100644
index 0000000..3449177
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/regex.json
@@ -0,0 +1,48 @@
+[
+ {
+ "description": "validation of regular expressions",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid regular expression",
+ "data": "([abc])+\\s+$",
+ "valid": true
+ },
+ {
+ "description": "a regular expression with unclosed parens is invalid",
+ "data": "^(abc]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/relative-json-pointer.json b/json/tests/draft2019-09/optional/format/relative-json-pointer.json
new file mode 100644
index 0000000..9309986
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/relative-json-pointer.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of Relative JSON Pointers (RJP)",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid upwards RJP",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "a valid downwards RJP",
+ "data": "0/foo/bar",
+ "valid": true
+ },
+ {
+ "description": "a valid up and then down RJP, with array index",
+ "data": "2/0/baz/1/zip",
+ "valid": true
+ },
+ {
+ "description": "a valid RJP taking the member or index name",
+ "data": "0#",
+ "valid": true
+ },
+ {
+ "description": "an invalid RJP that is a valid JSON Pointer",
+ "data": "/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "negative prefix",
+ "data": "-1/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "## is not a valid json-pointer",
+ "data": "0##",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus json-pointer",
+ "data": "01/a",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus octothorpe",
+ "data": "01#",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/time.json b/json/tests/draft2019-09/optional/format/time.json
new file mode 100644
index 0000000..5011d78
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/time.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of time strings",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid time string",
+ "data": "08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with leap second, Zulu",
+ "data": "23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, zero time-offset",
+ "data": "23:59:60+00:00",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong hour)",
+ "data": "22:59:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong minute)",
+ "data": "23:58:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, positive time-offset",
+ "data": "01:29:60+01:30",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large positive time-offset",
+ "data": "23:29:60+23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong hour)",
+ "data": "23:59:60+01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong minute)",
+ "data": "23:59:60+00:30",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, negative time-offset",
+ "data": "15:59:60-08:00",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large negative time-offset",
+ "data": "00:29:60-23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong hour)",
+ "data": "23:59:60-01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong minute)",
+ "data": "23:59:60-00:30",
+ "valid": false
+ },
+ {
+ "description": "a valid time string with second fraction",
+ "data": "23:20:50.52Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with precise second fraction",
+ "data": "08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with plus offset",
+ "data": "08:30:06+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with minus offset",
+ "data": "08:30:06-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with case-insensitive Z",
+ "data": "08:30:06z",
+ "valid": true
+ },
+ {
+ "description": "an invalid time string with invalid hour",
+ "data": "24:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid minute",
+ "data": "00:60:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid second",
+ "data": "00:00:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset hour",
+ "data": "01:02:03+24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset minute",
+ "data": "01:02:03+00:60",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time with both Z and numoffset",
+ "data": "01:02:03Z+00:30",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset indicator",
+ "data": "08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "01:01:01,1111",
+ "valid": false
+ },
+ {
+ "description": "no time offset",
+ "data": "12:00:00",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/unknown.json b/json/tests/draft2019-09/optional/format/unknown.json
new file mode 100644
index 0000000..12339ae
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/unknown.json
@@ -0,0 +1,43 @@
+[
+ {
+ "description": "unknown format",
+ "schema": { "format": "unknown" },
+ "tests": [
+ {
+ "description": "unknown formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore strings",
+ "data": "string",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/uri-reference.json b/json/tests/draft2019-09/optional/format/uri-reference.json
new file mode 100644
index 0000000..7cdf228
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/uri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of URI References",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid relative URI Reference",
+ "data": "/abc",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI Reference",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "a valid URI Reference",
+ "data": "abc",
+ "valid": true
+ },
+ {
+ "description": "a valid URI fragment",
+ "data": "#fragment",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI fragment",
+ "data": "#frag\\ment",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/uri-template.json b/json/tests/draft2019-09/optional/format/uri-template.json
new file mode 100644
index 0000000..df355c5
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/uri-template.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "format: uri-template",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term}",
+ "valid": true
+ },
+ {
+ "description": "an invalid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term",
+ "valid": false
+ },
+ {
+ "description": "a valid uri-template without variables",
+ "data": "http://example.com/dictionary",
+ "valid": true
+ },
+ {
+ "description": "a valid relative uri-template",
+ "data": "dictionary/{term:1}/{term}",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/uri.json b/json/tests/draft2019-09/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/format/uuid.json b/json/tests/draft2019-09/optional/format/uuid.json
new file mode 100644
index 0000000..e54cbc0
--- /dev/null
+++ b/json/tests/draft2019-09/optional/format/uuid.json
@@ -0,0 +1,85 @@
+[
+ {
+ "description": "uuid format",
+ "schema": {
+ "format": "uuid"
+ },
+ "tests": [
+ {
+ "description": "all upper-case",
+ "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all lower-case",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380",
+ "valid": true
+ },
+ {
+ "description": "mixed case",
+ "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all zeroes is valid",
+ "data": "00000000-0000-0000-0000-000000000000",
+ "valid": true
+ },
+ {
+ "description": "wrong length",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638",
+ "valid": false
+ },
+ {
+ "description": "missing section",
+ "data": "2eb8aa08-aa98-11ea-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "bad characters (not hex)",
+ "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "no dashes",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too few dashes",
+ "data": "2eb8aa08aa98-11ea-b4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too many dashes",
+ "data": "2eb8-aa08-aa98-11ea-b4aa73b44-1d16380",
+ "valid": false
+ },
+ {
+ "description": "dashes in the wrong spot",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380----",
+ "valid": false
+ },
+ {
+ "description": "valid version 4",
+ "data": "98d80576-482e-427f-8434-7f86890ab222",
+ "valid": true
+ },
+ {
+ "description": "valid version 5",
+ "data": "99c17cbb-656f-564a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 6",
+ "data": "99c17cbb-656f-664a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 15",
+ "data": "99c17cbb-656f-f64a-940f-1a4568f03487",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/non-bmp-regex.json b/json/tests/draft2019-09/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft2019-09/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/optional/refOfUnknownKeyword.json b/json/tests/draft2019-09/optional/refOfUnknownKeyword.json
new file mode 100644
index 0000000..5b150df
--- /dev/null
+++ b/json/tests/draft2019-09/optional/refOfUnknownKeyword.json
@@ -0,0 +1,44 @@
+[
+ {
+ "description": "reference of a root arbitrary keyword ",
+ "schema": {
+ "unknown-keyword": {"type": "integer"},
+ "properties": {
+ "bar": {"$ref": "#/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "reference of an arbitrary keyword of a sub-schema",
+ "schema": {
+ "properties": {
+ "foo": {"unknown-keyword": {"type": "integer"}},
+ "bar": {"$ref": "#/properties/foo/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/pattern.json b/json/tests/draft2019-09/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft2019-09/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/patternProperties.json b/json/tests/draft2019-09/patternProperties.json
new file mode 100644
index 0000000..c10ffcc
--- /dev/null
+++ b/json/tests/draft2019-09/patternProperties.json
@@ -0,0 +1,156 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "patternProperties with boolean schemas",
+ "schema": {
+ "patternProperties": {
+ "f.*": true,
+ "b.*": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property matching schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with a property matching both true and false is invalid",
+ "data": {"foobar":1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/properties.json b/json/tests/draft2019-09/properties.json
new file mode 100644
index 0000000..b86c181
--- /dev/null
+++ b/json/tests/draft2019-09/properties.json
@@ -0,0 +1,167 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with boolean schema",
+ "schema": {
+ "properties": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "no property present is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "only 'true' property present is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "only 'false' property present is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "both properties present is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/propertyNames.json b/json/tests/draft2019-09/propertyNames.json
new file mode 100644
index 0000000..f0788e6
--- /dev/null
+++ b/json/tests/draft2019-09/propertyNames.json
@@ -0,0 +1,107 @@
+[
+ {
+ "description": "propertyNames validation",
+ "schema": {
+ "propertyNames": {"maxLength": 3}
+ },
+ "tests": [
+ {
+ "description": "all property names valid",
+ "data": {
+ "f": {},
+ "foo": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "some property names invalid",
+ "data": {
+ "foo": {},
+ "foobar": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3, 4],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames validation with pattern",
+ "schema": {
+ "propertyNames": { "pattern": "^a+$" }
+ },
+ "tests": [
+ {
+ "description": "matching property names valid",
+ "data": {
+ "a": {},
+ "aa": {},
+ "aaa": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "non-matching property name is invalid",
+ "data": {
+ "aaA": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema true",
+ "schema": {"propertyNames": true},
+ "tests": [
+ {
+ "description": "object with any properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema false",
+ "schema": {"propertyNames": false},
+ "tests": [
+ {
+ "description": "object with any properties is invalid",
+ "data": {"foo": 1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/recursiveRef.json b/json/tests/draft2019-09/recursiveRef.json
new file mode 100644
index 0000000..ebb098c
--- /dev/null
+++ b/json/tests/draft2019-09/recursiveRef.json
@@ -0,0 +1,399 @@
+[
+ {
+ "description": "$recursiveRef without $recursiveAnchor works like $ref",
+ "schema": {
+ "properties": {
+ "foo": { "$recursiveRef": "#" }
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": { "foo": { "foo": false } },
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": { "bar": false },
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": { "foo": { "bar": false } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef without using nesting",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef2/schema.json",
+ "$defs": {
+ "myobject": {
+ "$id": "myobject.json",
+ "$recursiveAnchor": true,
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" }
+ }
+ ]
+ }
+ },
+ "anyOf": [
+ { "type": "integer" },
+ { "$ref": "#/$defs/myobject" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "integer matches at the outer level",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "single level match",
+ "data": { "foo": "hi" },
+ "valid": true
+ },
+ {
+ "description": "integer does not match as a property value",
+ "data": { "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "two levels, properties match with inner definition",
+ "data": { "foo": { "bar": "hi" } },
+ "valid": true
+ },
+ {
+ "description": "two levels, no match",
+ "data": { "foo": { "bar": 1 } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef with nesting",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef3/schema.json",
+ "$recursiveAnchor": true,
+ "$defs": {
+ "myobject": {
+ "$id": "myobject.json",
+ "$recursiveAnchor": true,
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" }
+ }
+ ]
+ }
+ },
+ "anyOf": [
+ { "type": "integer" },
+ { "$ref": "#/$defs/myobject" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "integer matches at the outer level",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "single level match",
+ "data": { "foo": "hi" },
+ "valid": true
+ },
+ {
+ "description": "integer now matches as a property value",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "two levels, properties match with inner definition",
+ "data": { "foo": { "bar": "hi" } },
+ "valid": true
+ },
+ {
+ "description": "two levels, properties match with $recursiveRef",
+ "data": { "foo": { "bar": 1 } },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef with $recursiveAnchor: false works like $ref",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef4/schema.json",
+ "$recursiveAnchor": false,
+ "$defs": {
+ "myobject": {
+ "$id": "myobject.json",
+ "$recursiveAnchor": false,
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" }
+ }
+ ]
+ }
+ },
+ "anyOf": [
+ { "type": "integer" },
+ { "$ref": "#/$defs/myobject" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "integer matches at the outer level",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "single level match",
+ "data": { "foo": "hi" },
+ "valid": true
+ },
+ {
+ "description": "integer does not match as a property value",
+ "data": { "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "two levels, properties match with inner definition",
+ "data": { "foo": { "bar": "hi" } },
+ "valid": true
+ },
+ {
+ "description": "two levels, integer does not match as a property value",
+ "data": { "foo": { "bar": 1 } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef with no $recursiveAnchor works like $ref",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef5/schema.json",
+ "$defs": {
+ "myobject": {
+ "$id": "myobject.json",
+ "$recursiveAnchor": false,
+ "anyOf": [
+ { "type": "string" },
+ {
+ "type": "object",
+ "additionalProperties": { "$recursiveRef": "#" }
+ }
+ ]
+ }
+ },
+ "anyOf": [
+ { "type": "integer" },
+ { "$ref": "#/$defs/myobject" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "integer matches at the outer level",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "single level match",
+ "data": { "foo": "hi" },
+ "valid": true
+ },
+ {
+ "description": "integer does not match as a property value",
+ "data": { "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "two levels, properties match with inner definition",
+ "data": { "foo": { "bar": "hi" } },
+ "valid": true
+ },
+ {
+ "description": "two levels, integer does not match as a property value",
+ "data": { "foo": { "bar": 1 } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef with no $recursiveAnchor in the initial target schema resource",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef6/base.json",
+ "$recursiveAnchor": true,
+ "anyOf": [
+ { "type": "boolean" },
+ {
+ "type": "object",
+ "additionalProperties": {
+ "$id": "http://localhost:4242/recursiveRef6/inner.json",
+ "$comment": "there is no $recursiveAnchor: true here, so we do NOT recurse to the base",
+ "anyOf": [
+ { "type": "integer" },
+ { "type": "object", "additionalProperties": { "$recursiveRef": "#" } }
+ ]
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "leaf node does not match; no recursion",
+ "data": { "foo": true },
+ "valid": false
+ },
+ {
+ "description": "leaf node matches: recursion uses the inner schema",
+ "data": { "foo": { "bar": 1 } },
+ "valid": true
+ },
+ {
+ "description": "leaf node does not match: recursion uses the inner schema",
+ "data": { "foo": { "bar": true } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$recursiveRef with no $recursiveAnchor in the outer schema resource",
+ "schema": {
+ "$id": "http://localhost:4242/recursiveRef7/base.json",
+ "anyOf": [
+ { "type": "boolean" },
+ {
+ "type": "object",
+ "additionalProperties": {
+ "$id": "http://localhost:4242/recursiveRef7/inner.json",
+ "$recursiveAnchor": true,
+ "anyOf": [
+ { "type": "integer" },
+ { "type": "object", "additionalProperties": { "$recursiveRef": "#" } }
+ ]
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "leaf node does not match; no recursion",
+ "data": { "foo": true },
+ "valid": false
+ },
+ {
+ "description": "leaf node matches: recursion only uses inner schema",
+ "data": { "foo": { "bar": 1 } },
+ "valid": true
+ },
+ {
+ "description": "leaf node does not match: recursion only uses inner schema",
+ "data": { "foo": { "bar": true } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dynamic paths to the $recursiveRef keyword",
+ "schema": {
+ "$id": "recursiveRef8_main.json",
+ "$defs": {
+ "inner": {
+ "$id": "recursiveRef8_inner.json",
+ "$recursiveAnchor": true,
+ "title": "inner",
+ "additionalProperties": {
+ "$recursiveRef": "#"
+ }
+ }
+ },
+ "if": {
+ "propertyNames": {
+ "pattern": "^[a-m]"
+ }
+ },
+ "then": {
+ "title": "any type of node",
+ "$id": "recursiveRef8_anyLeafNode.json",
+ "$recursiveAnchor": true,
+ "$ref": "recursiveRef8_inner.json"
+ },
+ "else": {
+ "title": "integer node",
+ "$id": "recursiveRef8_integerNode.json",
+ "$recursiveAnchor": true,
+ "type": [ "object", "integer" ],
+ "$ref": "recursiveRef8_inner.json"
+ }
+ },
+ "tests": [
+ {
+ "description": "recurse to anyLeafNode - floats are allowed",
+ "data": { "alpha": 1.1 },
+ "valid": true
+ },
+ {
+ "description": "recurse to integerNode - floats are not allowed",
+ "data": { "november": 1.1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dynamic $recursiveRef destination (not predictable at schema compile time)",
+ "schema": {
+ "$id": "main.json",
+ "$defs": {
+ "inner": {
+ "$id": "inner.json",
+ "$recursiveAnchor": true,
+ "title": "inner",
+ "additionalProperties": {
+ "$recursiveRef": "#"
+ }
+ }
+
+ },
+ "if": { "propertyNames": { "pattern": "^[a-m]" } },
+ "then": {
+ "title": "any type of node",
+ "$id": "anyLeafNode.json",
+ "$recursiveAnchor": true,
+ "$ref": "main.json#/$defs/inner"
+ },
+ "else": {
+ "title": "integer node",
+ "$id": "integerNode.json",
+ "$recursiveAnchor": true,
+ "type": [ "object", "integer" ],
+ "$ref": "main.json#/$defs/inner"
+ }
+ },
+ "tests": [
+ {
+ "description": "numeric node",
+ "data": { "alpha": 1.1 },
+ "valid": true
+ },
+ {
+ "description": "integer node",
+ "data": { "november": 1.1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/ref.json b/json/tests/draft2019-09/ref.json
new file mode 100644
index 0000000..fd8a3b2
--- /dev/null
+++ b/json/tests/draft2019-09/ref.json
@@ -0,0 +1,579 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "$defs": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/$defs/tilde~0field"},
+ "slash": {"$ref": "#/$defs/slash~1field"},
+ "percent": {"$ref": "#/$defs/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "$defs": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/$defs/a"},
+ "c": {"$ref": "#/$defs/b"}
+ },
+ "$ref": "#/$defs/c"
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref applies alongside sibling keywords",
+ "schema": {
+ "$defs": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid, maxItems valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems invalid",
+ "data": { "foo": [1, 2, 3] },
+ "valid": false
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {"$ref": "https://json-schema.org/draft/2019-09/schema"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/$defs/is-string"}
+ },
+ "$defs": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema true",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema false",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": false
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "$id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "$defs": {
+ "node": {
+ "$id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/$defs/foo%22bar"}
+ },
+ "$defs": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref creates new scope when adjacent to keywords",
+ "schema": {
+ "$defs": {
+ "A": {
+ "unevaluatedProperties": false
+ }
+ },
+ "properties": {
+ "prop1": {
+ "type": "string"
+ }
+ },
+ "$ref": "#/$defs/A"
+ },
+ "tests": [
+ {
+ "description": "referenced subschema doesn't see annotations from properties",
+ "data": {
+ "prop1": "match"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "$defs": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/$defs/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "do not evaluate the $ref inside the enum, definition exact match",
+ "data": { "type": "string" },
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/$defs/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "refs with relative uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-relative-uri-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "schema-relative-uri-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-relative-uri-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative refs with absolute uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-refs-absolute-uris-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id must be resolved against nearest parent, not just immediate parent",
+ "schema": {
+ "$id": "http://example.com/a.json",
+ "$defs": {
+ "x": {
+ "$id": "http://example.com/b/c.json",
+ "not": {
+ "$defs": {
+ "y": {
+ "$id": "d.json",
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "http://example.com/b/d.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number should pass",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number should fail",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/refRemote.json b/json/tests/draft2019-09/refRemote.json
new file mode 100644
index 0000000..b607432
--- /dev/null
+++ b/json/tests/draft2019-09/refRemote.json
@@ -0,0 +1,190 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "$id": "http://localhost:1234/",
+ "items": {
+ "$id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolder/"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolderInSubschema/",
+ "$defs": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "$id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name-defs.json#/$defs/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref with ref to defs",
+ "schema": {
+ "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json",
+ "$ref": "ref-and-defs.json"
+ },
+ "tests": [
+ {
+ "description": "invalid",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid",
+ "data": {
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/required.json b/json/tests/draft2019-09/required.json
new file mode 100644
index 0000000..abf18f3
--- /dev/null
+++ b/json/tests/draft2019-09/required.json
@@ -0,0 +1,105 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with empty array",
+ "schema": {
+ "properties": {
+ "foo": {}
+ },
+ "required": []
+ },
+ "tests": [
+ {
+ "description": "property not required",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/type.json b/json/tests/draft2019-09/type.json
new file mode 100644
index 0000000..8304647
--- /dev/null
+++ b/json/tests/draft2019-09/type.json
@@ -0,0 +1,474 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is an integer",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number (and an integer)",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/unevaluatedItems.json b/json/tests/draft2019-09/unevaluatedItems.json
new file mode 100644
index 0000000..afde31d
--- /dev/null
+++ b/json/tests/draft2019-09/unevaluatedItems.json
@@ -0,0 +1,525 @@
+[
+ {
+ "description": "unevaluatedItems true",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems false",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems as schema",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": { "type": "string" }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated items",
+ "data": [42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with uniform items",
+ "schema": {
+ "type": "array",
+ "items": { "type": "string" },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", "bar"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with tuple",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with additionalItems",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "type": "string" }
+ ],
+ "additionalItems": true,
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested tuple",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "type": "string" }
+ ],
+ "allOf": [
+ {
+ "items": [
+ true,
+ { "type": "number" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", 42],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", 42, true],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested additionalItems",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "items": [
+ { "type": "string" }
+ ],
+ "additionalItems": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested unevaluatedItems",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "items": [
+ { "type": "string" }
+ ]
+ },
+ {
+ "unevaluatedItems": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with anyOf",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "const": "foo" }
+ ],
+ "anyOf": [
+ {
+ "items": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "items": [
+ true,
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when one schema matches and has no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "when one schema matches and has unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ },
+ {
+ "description": "when two schemas match and has no unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "when two schemas match and has unevaluated items",
+ "data": ["foo", "bar", "baz", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with oneOf",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "const": "foo" }
+ ],
+ "oneOf": [
+ {
+ "items": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "items": [
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with not",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "const": "foo" }
+ ],
+ "not": {
+ "not": {
+ "items": [
+ true,
+ { "const": "bar" }
+ ]
+ }
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with if/then/else",
+ "schema": {
+ "type": "array",
+ "items": [
+ { "const": "foo" }
+ ],
+ "if": {
+ "items": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ "then": {
+ "items": [
+ true,
+ true,
+ { "const": "then" }
+ ]
+ },
+ "else": {
+ "items": [
+ true,
+ true,
+ true,
+ { "const": "else" }
+ ]
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when if matches and it has no unevaluated items",
+ "data": ["foo", "bar", "then"],
+ "valid": true
+ },
+ {
+ "description": "when if matches and it has unevaluated items",
+ "data": ["foo", "bar", "then", "else"],
+ "valid": false
+ },
+ {
+ "description": "when if doesn't match and it has no unevaluated items",
+ "data": ["foo", 42, 42, "else"],
+ "valid": true
+ },
+ {
+ "description": "when if doesn't match and it has unevaluated items",
+ "data": ["foo", 42, 42, "else", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with boolean schemas",
+ "schema": {
+ "type": "array",
+ "allOf": [true],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with $ref",
+ "schema": {
+ "type": "array",
+ "$ref": "#/$defs/bar",
+ "items": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false,
+ "$defs": {
+ "bar": {
+ "items": [
+ true,
+ { "type": "string" }
+ ]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "items": [ true ]
+ },
+ {
+ "unevaluatedItems": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": [ 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "item is evaluated in an uncle schema to unevaluatedItems",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "array",
+ "items": [
+ {
+ "type": "string"
+ }
+ ],
+ "unevaluatedItems": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "items": [
+ true,
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra items",
+ "data": {
+ "foo": [
+ "test"
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": [
+ "test",
+ "test"
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-array instances are valid",
+ "schema": {"unevaluatedItems": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/unevaluatedProperties.json b/json/tests/draft2019-09/unevaluatedProperties.json
new file mode 100644
index 0000000..6384cb8
--- /dev/null
+++ b/json/tests/draft2019-09/unevaluatedProperties.json
@@ -0,0 +1,1347 @@
+[
+ {
+ "description": "unevaluatedProperties true",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties schema",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": {
+ "type": "string",
+ "minLength": 3
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated properties",
+ "data": {
+ "foo": "fo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties false",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent patternProperties",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "additionalProperties": true,
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested patternProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "patternProperties": {
+ "^bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "additionalProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested unevaluatedProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": {
+ "type": "string",
+ "maxLength": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with anyOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ },
+ {
+ "properties": {
+ "quux": { "const": "quux" }
+ },
+ "required": ["quux"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when one matches and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when one matches and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "not-baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when two match and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when two match and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz",
+ "quux": "not-quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with oneOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "oneOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "quux": "quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with not",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "not": {
+ "not": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, then not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, else not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with dependentSchemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "dependentSchemas": {
+ "foo": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with boolean schemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [true],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with $ref",
+ "schema": {
+ "type": "object",
+ "$ref": "#/$defs/bar",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false,
+ "$defs": {
+ "bar": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, true with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, false with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ },
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property is evaluated in an uncle schema to unevaluatedProperties",
+ "comment": "see https://stackoverflow.com/questions/66936884/deeply-nested-unevaluatedproperties-and-their-expectations",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "object",
+ "properties": {
+ "bar": {
+ "type": "string"
+ }
+ },
+ "unevaluatedProperties": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "properties": {
+ "faz": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra properties",
+ "data": {
+ "foo": {
+ "bar": "test"
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": {
+ "bar": "test",
+ "faz": "test"
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, allOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, anyOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + single cyclic ref",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "x": { "$ref": "#" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "Single is valid",
+ "data": { "x": {} },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 1st level is invalid",
+ "data": { "x": {}, "y": {} },
+ "valid": false
+ },
+ {
+ "description": "Nested is valid",
+ "data": { "x": { "x": {} } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 2nd level is invalid",
+ "data": { "x": { "x": {}, "y": {} } },
+ "valid": false
+ },
+ {
+ "description": "Deep nested is valid",
+ "data": { "x": { "x": { "x": {} } } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 3rd level is invalid",
+ "data": { "x": { "x": { "x": {}, "y": {} } } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + ref inside allOf / oneOf",
+ "schema": {
+ "$defs": {
+ "one": {
+ "properties": { "a": true }
+ },
+ "two": {
+ "required": ["x"],
+ "properties": { "x": true }
+ }
+ },
+ "allOf": [
+ { "$ref": "#/$defs/one" },
+ { "properties": { "b": true } },
+ {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ {
+ "required": ["y"],
+ "properties": { "y": true }
+ }
+ ]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid (no x or y)",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a and b are invalid (no x or y)",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "x and y are invalid",
+ "data": { "x": 1, "y": 1 },
+ "valid": false
+ },
+ {
+ "description": "a and x are valid",
+ "data": { "a": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and y are valid",
+ "data": { "a": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x are valid",
+ "data": { "a": 1, "b": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and y are valid",
+ "data": { "a": 1, "b": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x and y are invalid",
+ "data": { "a": 1, "b": 1, "x": 1, "y": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dynamic evalation inside nested refs",
+ "schema": {
+ "$defs": {
+ "one": {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ { "required": ["b"], "properties": { "b": true } },
+ { "required": ["xx"], "patternProperties": { "x": true } },
+ { "required": ["all"], "unevaluatedProperties": true }
+ ]
+ },
+ "two": {
+ "oneOf": [
+ { "required": ["c"], "properties": { "c": true } },
+ { "required": ["d"], "properties": { "d": true } }
+ ]
+ }
+ },
+ "oneOf": [
+ { "$ref": "#/$defs/one" },
+ { "required": ["a"], "properties": { "a": true } }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a is valid",
+ "data": { "a": 1 },
+ "valid": true
+ },
+ {
+ "description": "b is valid",
+ "data": { "b": 1 },
+ "valid": true
+ },
+ {
+ "description": "c is valid",
+ "data": { "c": 1 },
+ "valid": true
+ },
+ {
+ "description": "d is valid",
+ "data": { "d": 1 },
+ "valid": true
+ },
+ {
+ "description": "a + b is invalid",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + c is invalid",
+ "data": { "a": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + d is invalid",
+ "data": { "a": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + c is invalid",
+ "data": { "b": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + d is invalid",
+ "data": { "b": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "c + d is invalid",
+ "data": { "c": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx is valid",
+ "data": { "xx": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foox is valid",
+ "data": { "xx": 1, "foox": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foo is invalid",
+ "data": { "xx": 1, "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + a is invalid",
+ "data": { "xx": 1, "a": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + b is invalid",
+ "data": { "xx": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + c is invalid",
+ "data": { "xx": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + d is invalid",
+ "data": { "xx": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "all is valid",
+ "data": { "all": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + foo is valid",
+ "data": { "all": 1, "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + a is invalid",
+ "data": { "all": 1, "a": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-object instances are valid",
+ "schema": {"unevaluatedProperties": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/uniqueItems.json b/json/tests/draft2019-09/uniqueItems.json
new file mode 100644
index 0000000..2ccf666
--- /dev/null
+++ b/json/tests/draft2019-09/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/unknownKeyword.json b/json/tests/draft2019-09/unknownKeyword.json
new file mode 100644
index 0000000..e46657d
--- /dev/null
+++ b/json/tests/draft2019-09/unknownKeyword.json
@@ -0,0 +1,56 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "$defs": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_unknown0" },
+ { "$ref": "#/$defs/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2019-09/vocabulary.json b/json/tests/draft2019-09/vocabulary.json
new file mode 100644
index 0000000..982e673
--- /dev/null
+++ b/json/tests/draft2019-09/vocabulary.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "schema that uses custom metaschema with with no validation vocabulary",
+ "schema": {
+ "$id": "https://schema/using/no/validation",
+ "$schema": "http://localhost:1234/draft2019-09/metaschema-no-validation.json",
+ "properties": {
+ "badProperty": false,
+ "numberProperty": {
+ "minimum": 10
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "applicator vocabulary still works",
+ "data": {
+ "badProperty": "this property should not exist"
+ },
+ "valid": false
+ },
+ {
+ "description": "no validation: valid number",
+ "data": {
+ "numberProperty": 20
+ },
+ "valid": true
+ },
+ {
+ "description": "no validation: invalid number, but it still validates",
+ "data": {
+ "numberProperty": 1
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/additionalProperties.json b/json/tests/draft2020-12/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft2020-12/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/allOf.json b/json/tests/draft2020-12/allOf.json
new file mode 100644
index 0000000..ec9319e
--- /dev/null
+++ b/json/tests/draft2020-12/allOf.json
@@ -0,0 +1,294 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all true",
+ "schema": {"allOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, some false",
+ "schema": {"allOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all false",
+ "schema": {"allOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/anchor.json b/json/tests/draft2020-12/anchor.json
new file mode 100644
index 0000000..416c224
--- /dev/null
+++ b/json/tests/draft2020-12/anchor.json
@@ -0,0 +1,173 @@
+[
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with absolute URI",
+ "schema": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar",
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$anchor": "foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$anchor inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $anchor buried in the enum",
+ "schema": {
+ "$defs": {
+ "anchor_in_enum": {
+ "enum": [
+ {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ ]
+ },
+ "real_identifier_in_schema": {
+ "$anchor": "my_anchor",
+ "type": "string"
+ },
+ "zzz_anchor_in_const": {
+ "const": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/anchor_in_enum" },
+ { "$ref": "#my_anchor" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "in implementations that strip $anchor, this may match either $def",
+ "data": {
+ "type": "null"
+ },
+ "valid": false
+ },
+ {
+ "description": "match $ref to $anchor",
+ "data": "a string to match #/$defs/anchor_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $anchor",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "same $anchor with different base uri",
+ "schema": {
+ "$id": "http://localhost:1234/foobar",
+ "$defs": {
+ "A": {
+ "$id": "child1",
+ "allOf": [
+ {
+ "$id": "child2",
+ "$anchor": "my_anchor",
+ "type": "number"
+ },
+ {
+ "$anchor": "my_anchor",
+ "type": "string"
+ }
+ ]
+ }
+ },
+ "$ref": "child1#my_anchor"
+ },
+ "tests": [
+ {
+ "description": "$ref should resolve to /$defs/A/allOf/1",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "$ref should not resolve to /$defs/A/allOf/0",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/anyOf.json b/json/tests/draft2020-12/anyOf.json
new file mode 100644
index 0000000..ab5eb38
--- /dev/null
+++ b/json/tests/draft2020-12/anyOf.json
@@ -0,0 +1,189 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all true",
+ "schema": {"anyOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, some true",
+ "schema": {"anyOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all false",
+ "schema": {"anyOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/boolean_schema.json b/json/tests/draft2020-12/boolean_schema.json
new file mode 100644
index 0000000..6d40f23
--- /dev/null
+++ b/json/tests/draft2020-12/boolean_schema.json
@@ -0,0 +1,104 @@
+[
+ {
+ "description": "boolean schema 'true'",
+ "schema": true,
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "boolean true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "boolean false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean schema 'false'",
+ "schema": false,
+ "tests": [
+ {
+ "description": "number is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "boolean true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "boolean false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/const.json b/json/tests/draft2020-12/const.json
new file mode 100644
index 0000000..1c2cafc
--- /dev/null
+++ b/json/tests/draft2020-12/const.json
@@ -0,0 +1,342 @@
+[
+ {
+ "description": "const validation",
+ "schema": {"const": 2},
+ "tests": [
+ {
+ "description": "same value is valid",
+ "data": 2,
+ "valid": true
+ },
+ {
+ "description": "another value is invalid",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with object",
+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
+ "tests": [
+ {
+ "description": "same object is valid",
+ "data": {"foo": "bar", "baz": "bax"},
+ "valid": true
+ },
+ {
+ "description": "same object with different property order is valid",
+ "data": {"baz": "bax", "foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "another object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": [1, 2],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with array",
+ "schema": {"const": [{ "foo": "bar" }]},
+ "tests": [
+ {
+ "description": "same array is valid",
+ "data": [{"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "another array item is invalid",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "array with additional items is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with null",
+ "schema": {"const": null},
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "not null is invalid",
+ "data": 0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with false does not match 0",
+ "schema": {"const": false},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with true does not match 1",
+ "schema": {"const": true},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [false] does not match [0]",
+ "schema": {"const": [false]},
+ "tests": [
+ {
+ "description": "[false] is valid",
+ "data": [false],
+ "valid": true
+ },
+ {
+ "description": "[0] is invalid",
+ "data": [0],
+ "valid": false
+ },
+ {
+ "description": "[0.0] is invalid",
+ "data": [0.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [true] does not match [1]",
+ "schema": {"const": [true]},
+ "tests": [
+ {
+ "description": "[true] is valid",
+ "data": [true],
+ "valid": true
+ },
+ {
+ "description": "[1] is invalid",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "[1.0] is invalid",
+ "data": [1.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": false} does not match {\"a\": 0}",
+ "schema": {"const": {"a": false}},
+ "tests": [
+ {
+ "description": "{\"a\": false} is valid",
+ "data": {"a": false},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 0} is invalid",
+ "data": {"a": 0},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 0.0} is invalid",
+ "data": {"a": 0.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": true} does not match {\"a\": 1}",
+ "schema": {"const": {"a": true}},
+ "tests": [
+ {
+ "description": "{\"a\": true} is valid",
+ "data": {"a": true},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 1} is invalid",
+ "data": {"a": 1},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 1.0} is invalid",
+ "data": {"a": 1.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 0 does not match other zero-like types",
+ "schema": {"const": 0},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "empty string is invalid",
+ "data": "",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 1 does not match true",
+ "schema": {"const": 1},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "const with -2.0 matches integer and float types",
+ "schema": {"const": -2.0},
+ "tests": [
+ {
+ "description": "integer -2 is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "integer 2 is invalid",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "float -2.0 is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float 2.0 is invalid",
+ "data": 2.0,
+ "valid": false
+ },
+ {
+ "description": "float -2.00001 is invalid",
+ "data": -2.00001,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "float and integers are equal up to 64-bit representation limits",
+ "schema": {"const": 9007199254740992},
+ "tests": [
+ {
+ "description": "integer is valid",
+ "data": 9007199254740992,
+ "valid": true
+ },
+ {
+ "description": "integer minus one is invalid",
+ "data": 9007199254740991,
+ "valid": false
+ },
+ {
+ "description": "float is valid",
+ "data": 9007199254740992.0,
+ "valid": true
+ },
+ {
+ "description": "float minus one is invalid",
+ "data": 9007199254740991.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "const": "hello\u0000there" },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/contains.json b/json/tests/draft2020-12/contains.json
new file mode 100644
index 0000000..215da98
--- /dev/null
+++ b/json/tests/draft2020-12/contains.json
@@ -0,0 +1,150 @@
+[
+ {
+ "description": "contains keyword validation",
+ "schema": {
+ "contains": {"minimum": 5}
+ },
+ "tests": [
+ {
+ "description": "array with item matching schema (5) is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with item matching schema (6) is valid",
+ "data": [3, 4, 6],
+ "valid": true
+ },
+ {
+ "description": "array with two items matching schema (5, 6) is valid",
+ "data": [3, 4, 5, 6],
+ "valid": true
+ },
+ {
+ "description": "array without items matching schema is invalid",
+ "data": [2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "not array is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with const keyword",
+ "schema": {
+ "contains": { "const": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item 5 is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with two items 5 is valid",
+ "data": [3, 4, 5, 5],
+ "valid": true
+ },
+ {
+ "description": "array without item 5 is invalid",
+ "data": [1, 2, 3, 4],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema true",
+ "schema": {"contains": true},
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema false",
+ "schema": {"contains": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "non-arrays are valid",
+ "data": "contains does not apply to strings",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items + contains",
+ "schema": {
+ "items": { "multipleOf": 2 },
+ "contains": { "multipleOf": 3 }
+ },
+ "tests": [
+ {
+ "description": "matches items, does not match contains",
+ "data": [ 2, 4, 8 ],
+ "valid": false
+ },
+ {
+ "description": "does not match items, matches contains",
+ "data": [ 3, 6, 9 ],
+ "valid": false
+ },
+ {
+ "description": "matches both items and contains",
+ "data": [ 6, 12 ],
+ "valid": true
+ },
+ {
+ "description": "matches neither items nor contains",
+ "data": [ 1, 5 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains with false if subschema",
+ "schema": {
+ "contains": {
+ "if": false,
+ "else": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/content.json b/json/tests/draft2020-12/content.json
new file mode 100644
index 0000000..44688e8
--- /dev/null
+++ b/json/tests/draft2020-12/content.json
@@ -0,0 +1,127 @@
+[
+ {
+ "description": "validation of string-encoded content based on media type",
+ "schema": {
+ "contentMediaType": "application/json"
+ },
+ "tests": [
+ {
+ "description": "a valid JSON document",
+ "data": "{\"foo\": \"bar\"}",
+ "valid": true
+ },
+ {
+ "description": "an invalid JSON document; validates true",
+ "data": "{:}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary string-encoding",
+ "schema": {
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64 string",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string (% is not a valid character); validates true",
+ "data": "eyJmb28iOi%iYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents with schema",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64",
+ "contentSchema": { "required": ["foo"], "properties": { "foo": { "type": "string" } } }
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "another valid base64-encoded JSON document",
+ "data": "eyJib28iOiAyMCwgImZvbyI6ICJiYXoifQ==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64-encoded JSON document; validates true",
+ "data": "eyJib28iOiAyMH0=",
+ "valid": true
+ },
+ {
+ "description": "an empty object as a base64-encoded JSON document; validates true",
+ "data": "e30=",
+ "valid": true
+ },
+ {
+ "description": "an empty array as a base64-encoded JSON document",
+ "data": "W10=",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document; validates true",
+ "data": "ezp9Cg==",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON; validates true",
+ "data": "{}",
+ "valid": true
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/default.json b/json/tests/draft2020-12/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft2020-12/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/defs.json b/json/tests/draft2020-12/defs.json
new file mode 100644
index 0000000..c738be2
--- /dev/null
+++ b/json/tests/draft2020-12/defs.json
@@ -0,0 +1,20 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
+ },
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {"$defs": {"foo": {"type": "integer"}}},
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {"$defs": {"foo": {"type": 1}}},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/dependentRequired.json b/json/tests/draft2020-12/dependentRequired.json
new file mode 100644
index 0000000..c817120
--- /dev/null
+++ b/json/tests/draft2020-12/dependentRequired.json
@@ -0,0 +1,142 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {"dependentRequired": {"bar": ["foo"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "empty dependents",
+ "schema": {"dependentRequired": {"bar": []}},
+ "tests": [
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "object with one property",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "non-object is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependents required",
+ "schema": {"dependentRequired": {"quux": ["foo", "bar"]}},
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentRequired": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "CRLF",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quotes",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "CRLF missing dependent",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quotes missing dependent",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/dependentSchemas.json b/json/tests/draft2020-12/dependentSchemas.json
new file mode 100644
index 0000000..2ba1a75
--- /dev/null
+++ b/json/tests/draft2020-12/dependentSchemas.json
@@ -0,0 +1,129 @@
+[
+ {
+ "description": "single dependency",
+ "schema": {
+ "dependentSchemas": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean subschemas",
+ "schema": {
+ "dependentSchemas": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property having schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property having schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependentSchemas": {
+ "foo\tbar": {"minProperties": 4},
+ "foo'bar": {"required": ["foo\"bar"]}
+ }
+ },
+ "tests": [
+ {
+ "description": "quoted tab",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "quoted quote",
+ "data": {
+ "foo'bar": {"foo\"bar": 1}
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted tab invalid under dependent schema",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "quoted quote invalid under dependent schema",
+ "data": {"foo'bar": 1},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/dynamicRef.json b/json/tests/draft2020-12/dynamicRef.json
new file mode 100644
index 0000000..618d836
--- /dev/null
+++ b/json/tests/draft2020-12/dynamicRef.json
@@ -0,0 +1,619 @@
+[
+ {
+ "description": "A $dynamicRef to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamicRef-dynamicAnchor-same-schema/root",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef to an $anchor in the same schema resource should behave like a normal $ref to an $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamicRef-anchor-same-schema/root",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "foo": {
+ "$anchor": "items",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $ref to a $dynamicAnchor in the same schema resource should behave like a normal $ref to an $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/ref-dynamicAnchor-same-schema/root",
+ "type": "array",
+ "items": { "$ref": "#items" },
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef should resolve to the first $dynamicAnchor still in scope that is encountered when the schema is evaluated",
+ "schema": {
+ "$id": "https://test.json-schema.org/typical-dynamic-resolution/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$comment": "This is only needed to satisfy the bookending requirement",
+ "$dynamicAnchor": "items"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef with intermediate scopes that don't include a matching $dynamicAnchor should not affect dynamic scope resolution",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-resolution-with-intermediate-scopes/root",
+ "$ref": "intermediate-scope",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "intermediate-scope": {
+ "$id": "intermediate-scope",
+ "$ref": "list"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$comment": "This is only needed to satisfy the bookending requirement",
+ "$dynamicAnchor": "items"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "An array of strings is valid",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "An array containing non-strings is invalid",
+ "data": ["foo", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "An $anchor with the same name as a $dynamicAnchor should not be used for dynamic scope resolution",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-resolution-ignores-anchors/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$anchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$comment": "This is only needed to satisfy the bookending requirement",
+ "$dynamicAnchor": "items"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "Any array is valid",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef without a matching $dynamicAnchor in the same schema resource should behave like a normal $ref to $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-resolution-without-bookend/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref",
+ "$anchor": "items"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "Any array is valid",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef with a non-matching $dynamicAnchor in the same schema resource should behave like a normal $ref to $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/unmatched-dynamic-anchor/root",
+ "$ref": "list",
+ "$defs": {
+ "foo": {
+ "$dynamicAnchor": "items",
+ "type": "string"
+ },
+ "list": {
+ "$id": "list",
+ "type": "array",
+ "items": { "$dynamicRef": "#items" },
+ "$defs": {
+ "items": {
+ "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref",
+ "$anchor": "items",
+ "$dynamicAnchor": "foo"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "Any array is valid",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef that initially resolves to a schema with a matching $dynamicAnchor should resolve to the first $dynamicAnchor in the dynamic scope",
+ "schema": {
+ "$id": "https://test.json-schema.org/relative-dynamic-reference/root",
+ "$dynamicAnchor": "meta",
+ "type": "object",
+ "properties": {
+ "foo": { "const": "pass" }
+ },
+ "$ref": "extended",
+ "$defs": {
+ "extended": {
+ "$id": "extended",
+ "$dynamicAnchor": "meta",
+ "type": "object",
+ "properties": {
+ "bar": { "$ref": "bar" }
+ }
+ },
+ "bar": {
+ "$id": "bar",
+ "type": "object",
+ "properties": {
+ "baz": { "$dynamicRef": "extended#meta" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "The recursive part is valid against the root",
+ "data": {
+ "foo": "pass",
+ "bar": {
+ "baz": { "foo": "pass" }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "The recursive part is not valid against the root",
+ "data": {
+ "foo": "pass",
+ "bar": {
+ "baz": { "foo": "fail" }
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "A $dynamicRef that initially resolves to a schema without a matching $dynamicAnchor should behave like a normal $ref to $anchor",
+ "schema": {
+ "$id": "https://test.json-schema.org/relative-dynamic-reference-without-bookend/root",
+ "$dynamicAnchor": "meta",
+ "type": "object",
+ "properties": {
+ "foo": { "const": "pass" }
+ },
+ "$ref": "extended",
+ "$defs": {
+ "extended": {
+ "$id": "extended",
+ "$anchor": "meta",
+ "type": "object",
+ "properties": {
+ "bar": { "$ref": "bar" }
+ }
+ },
+ "bar": {
+ "$id": "bar",
+ "type": "object",
+ "properties": {
+ "baz": { "$dynamicRef": "extended#meta" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "The recursive part doesn't need to validate against the root",
+ "data": {
+ "foo": "pass",
+ "bar": {
+ "baz": { "foo": "fail" }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dynamic paths to the $dynamicRef keyword",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-ref-with-multiple-paths/main",
+ "$defs": {
+ "inner": {
+ "$id": "inner",
+ "$dynamicAnchor": "foo",
+ "title": "inner",
+ "additionalProperties": {
+ "$dynamicRef": "#foo"
+ }
+ }
+ },
+ "if": {
+ "propertyNames": {
+ "pattern": "^[a-m]"
+ }
+ },
+ "then": {
+ "title": "any type of node",
+ "$id": "anyLeafNode",
+ "$dynamicAnchor": "foo",
+ "$ref": "inner"
+ },
+ "else": {
+ "title": "integer node",
+ "$id": "integerNode",
+ "$dynamicAnchor": "foo",
+ "type": [ "object", "integer" ],
+ "$ref": "inner"
+ }
+ },
+ "tests": [
+ {
+ "description": "recurse to anyLeafNode - floats are allowed",
+ "data": { "alpha": 1.1 },
+ "valid": true
+ },
+ {
+ "description": "recurse to integerNode - floats are not allowed",
+ "data": { "november": 1.1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "after leaving a dynamic scope, it should not be used by a $dynamicRef",
+ "schema": {
+ "$id": "https://test.json-schema.org/dynamic-ref-leaving-dynamic-scope/main",
+ "if": {
+ "$id": "first_scope",
+ "$defs": {
+ "thingy": {
+ "$comment": "this is first_scope#thingy",
+ "$dynamicAnchor": "thingy",
+ "type": "number"
+ }
+ }
+ },
+ "then": {
+ "$id": "second_scope",
+ "$ref": "start",
+ "$defs": {
+ "thingy": {
+ "$comment": "this is second_scope#thingy, the final destination of the $dynamicRef",
+ "$dynamicAnchor": "thingy",
+ "type": "null"
+ }
+ }
+ },
+ "$defs": {
+ "start": {
+ "$comment": "this is the landing spot from $ref",
+ "$id": "start",
+ "$dynamicRef": "inner_scope#thingy"
+ },
+ "thingy": {
+ "$comment": "this is the first stop for the $dynamicRef",
+ "$id": "inner_scope",
+ "$dynamicAnchor": "thingy",
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "string matches /$defs/thingy, but the $dynamicRef does not stop here",
+ "data": "a string",
+ "valid": false
+ },
+ {
+ "description": "first_scope is not in dynamic scope for the $dynamicRef",
+ "data": 42,
+ "valid": false
+ },
+ {
+ "description": "/then/$defs/thingy is the final stop for the $dynamicRef",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "strict-tree schema, guards against misspelled properties",
+ "schema": {
+ "$id": "http://localhost:1234/strict-tree.json",
+ "$dynamicAnchor": "node",
+
+ "$ref": "tree.json",
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "instance with misspelled field",
+ "data": {
+ "children": [{
+ "daat": 1
+ }]
+ },
+ "valid": false
+ },
+ {
+ "description": "instance with correct field",
+ "data": {
+ "children": [{
+ "data": 1
+ }]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "tests for implementation dynamic anchor and reference link",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible.json",
+ "$ref": "extendible-dynamic-ref.json",
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering.",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible-allof-defs-first.json",
+ "allOf": [
+ {
+ "$ref": "extendible-dynamic-ref.json"
+ },
+ {
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Tests for implementation dynamic anchor and reference link. Reference should be independent of any possible ordering.",
+ "schema": {
+ "$id": "http://localhost:1234/strict-extendible-allof-ref-first.json",
+ "allOf": [
+ {
+ "$defs": {
+ "elements": {
+ "$dynamicAnchor": "elements",
+ "properties": {
+ "a": true
+ },
+ "required": ["a"],
+ "additionalProperties": false
+ }
+ }
+ },
+ {
+ "$ref": "extendible-dynamic-ref.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "incorrect parent schema",
+ "data": {
+ "a": true
+ },
+ "valid": false
+ },
+ {
+ "description": "incorrect extended schema",
+ "data": {
+ "elements": [
+ { "b": 1 }
+ ]
+ },
+ "valid": false
+ },
+ {
+ "description": "correct extended schema",
+ "data": {
+ "elements": [
+ { "a": 1 }
+ ]
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/enum.json b/json/tests/draft2020-12/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft2020-12/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/exclusiveMaximum.json b/json/tests/draft2020-12/exclusiveMaximum.json
new file mode 100644
index 0000000..dc3cd70
--- /dev/null
+++ b/json/tests/draft2020-12/exclusiveMaximum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "exclusiveMaximum": 3.0
+ },
+ "tests": [
+ {
+ "description": "below the exclusiveMaximum is valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ },
+ {
+ "description": "above the exclusiveMaximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/exclusiveMinimum.json b/json/tests/draft2020-12/exclusiveMinimum.json
new file mode 100644
index 0000000..b38d7ec
--- /dev/null
+++ b/json/tests/draft2020-12/exclusiveMinimum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "exclusiveMinimum": 1.1
+ },
+ "tests": [
+ {
+ "description": "above the exclusiveMinimum is valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "below the exclusiveMinimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/format.json b/json/tests/draft2020-12/format.json
new file mode 100644
index 0000000..a4b51d2
--- /dev/null
+++ b/json/tests/draft2020-12/format.json
@@ -0,0 +1,686 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-email format",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "regex format",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-hostname format",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date format",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "time format",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "json-pointer format",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative-json-pointer format",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri format",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri-reference format",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-reference format",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-template format",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uuid format",
+ "schema": { "format": "uuid" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "duration format",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/id.json b/json/tests/draft2020-12/id.json
new file mode 100644
index 0000000..14a0e9c
--- /dev/null
+++ b/json/tests/draft2020-12/id.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "Invalid use of fragments in location-independent $id",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
+ },
+ "tests": [
+ {
+ "description": "Identifier name",
+ "data": {
+ "$ref": "#foo",
+ "$defs": {
+ "A": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name and no ref",
+ "data": {
+ "$defs": {
+ "A": { "$id": "#foo" }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path",
+ "data": {
+ "$ref": "#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#foo",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#/a/b",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#foo",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ },
+ {
+ "description": "Identifier path with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/a/b",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#/a/b",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Valid use of empty fragments in location-independent $id",
+ "comment": "These are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
+ },
+ "tests": [
+ {
+ "description": "Identifier name with absolute URI",
+ "data": {
+ "$ref": "http://localhost:1234/bar",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/bar#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Identifier name with base URI change in subschema",
+ "data": {
+ "$id": "http://localhost:1234/root",
+ "$ref": "http://localhost:1234/nested.json#/$defs/B",
+ "$defs": {
+ "A": {
+ "$id": "nested.json",
+ "$defs": {
+ "B": {
+ "$id": "#",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "Unnormalized $ids are allowed but discouraged",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
+ },
+ "tests": [
+ {
+ "description": "Unnormalized identifier",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment",
+ "data": {
+ "$ref": "http://localhost:1234/foo/baz",
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "Unnormalized identifier with empty fragment and no ref",
+ "data": {
+ "$defs": {
+ "A": {
+ "$id": "http://localhost:1234/foo/bar/../baz#",
+ "type": "integer"
+ }
+ }
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $id buried in the enum",
+ "schema": {
+ "$defs": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_enum" },
+ { "$ref": "https://localhost:1234/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to $id",
+ "data": "a string to match #/$defs/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/if-then-else.json b/json/tests/draft2020-12/if-then-else.json
new file mode 100644
index 0000000..284e919
--- /dev/null
+++ b/json/tests/draft2020-12/if-then-else.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "ignore if without then or else",
+ "schema": {
+ "if": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone if",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone if",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore then without if",
+ "schema": {
+ "then": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone then",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone then",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore else without if",
+ "schema": {
+ "else": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone else",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone else",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and then without else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid when if test fails",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and else without then",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when if test passes",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validate against correct branch, then vs else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-interference across combined schemas",
+ "schema": {
+ "allOf": [
+ {
+ "if": {
+ "exclusiveMaximum": 0
+ }
+ },
+ {
+ "then": {
+ "minimum": -10
+ }
+ },
+ {
+ "else": {
+ "multipleOf": 2
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid, but would have been invalid through then",
+ "data": -100,
+ "valid": true
+ },
+ {
+ "description": "valid, but would have been invalid through else",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema true",
+ "schema": {
+ "if": true,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema true in if always chooses the then path (valid)",
+ "data": "then",
+ "valid": true
+ },
+ {
+ "description": "boolean schema true in if always chooses the then path (invalid)",
+ "data": "else",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema false",
+ "schema": {
+ "if": false,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema false in if always chooses the else path (invalid)",
+ "data": "then",
+ "valid": false
+ },
+ {
+ "description": "boolean schema false in if always chooses the else path (valid)",
+ "data": "else",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if appears at the end when serialized (keyword processing sequence)",
+ "schema": {
+ "then": { "const": "yes" },
+ "else": { "const": "other" },
+ "if": { "maxLength": 4 }
+ },
+ "tests": [
+ {
+ "description": "yes redirects to then and passes",
+ "data": "yes",
+ "valid": true
+ },
+ {
+ "description": "other redirects to else and passes",
+ "data": "other",
+ "valid": true
+ },
+ {
+ "description": "no redirects to then and fails",
+ "data": "no",
+ "valid": false
+ },
+ {
+ "description": "invalid redirects to else and fails",
+ "data": "invalid",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/infinite-loop-detection.json b/json/tests/draft2020-12/infinite-loop-detection.json
new file mode 100644
index 0000000..9c3c362
--- /dev/null
+++ b/json/tests/draft2020-12/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "$defs": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/$defs/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/items.json b/json/tests/draft2020-12/items.json
new file mode 100644
index 0000000..b918194
--- /dev/null
+++ b/json/tests/draft2020-12/items.json
@@ -0,0 +1,256 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (true)",
+ "schema": {"items": true},
+ "tests": [
+ {
+ "description": "any array is valid",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (false)",
+ "schema": {"items": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": [ 1, "foo", true ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "$defs": {
+ "item": {
+ "type": "array",
+ "items": false,
+ "prefixItems": [
+ { "$ref": "#/$defs/sub-item" },
+ { "$ref": "#/$defs/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "items": false,
+ "prefixItems": [
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" },
+ { "$ref": "#/$defs/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "prefixItems with no additional items allowed",
+ "schema": {
+ "prefixItems": [{}, {}, {}],
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "prefixItems": [ { "minimum": 3 } ] }
+ ],
+ "items": { "minimum": 5 }
+ },
+ "tests": [
+ {
+ "description": "prefixItems in allOf should not constrain items, invalid case",
+ "data": [ 3, 5 ],
+ "valid": false
+ },
+ {
+ "description": "prefixItems in allOf should not constrain items, valid case",
+ "data": [ 5, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "prefixItems validation adjusts the starting index for items",
+ "schema": {
+ "prefixItems": [ { "type": "string" } ],
+ "items": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/maxContains.json b/json/tests/draft2020-12/maxContains.json
new file mode 100644
index 0000000..3c42fb3
--- /dev/null
+++ b/json/tests/draft2020-12/maxContains.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "maxContains without contains is ignored",
+ "schema": {
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "two items still valid against lone maxContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [ 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "some elements match, valid maxContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, invalid maxContains",
+ "data": [ 1, 2, 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains < maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 1,
+ "maxContains": 3
+ },
+ "tests": [
+ {
+ "description": "actual < minContains < maxContains",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "minContains < actual < maxContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "minContains < maxContains < actual",
+ "data": [ 1, 1, 1, 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/maxItems.json b/json/tests/draft2020-12/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft2020-12/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/maxLength.json b/json/tests/draft2020-12/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft2020-12/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/maxProperties.json b/json/tests/draft2020-12/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft2020-12/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/maximum.json b/json/tests/draft2020-12/maximum.json
new file mode 100644
index 0000000..6844a39
--- /dev/null
+++ b/json/tests/draft2020-12/maximum.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/minContains.json b/json/tests/draft2020-12/minContains.json
new file mode 100644
index 0000000..baf5b1e
--- /dev/null
+++ b/json/tests/draft2020-12/minContains.json
@@ -0,0 +1,197 @@
+[
+ {
+ "description": "minContains without contains is ignored",
+ "schema": {
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "one item valid against lone minContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "zero items still valid against lone minContains",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=1 with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "no elements match",
+ "data": [ 2 ],
+ "valid": false
+ },
+ {
+ "description": "single element matches, valid minContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains=2 with contains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "some elements match, invalid minContains",
+ "data": [ 1, 2 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid minContains (exactly as needed)",
+ "data": [ 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "all elements match, valid minContains (more than needed)",
+ "data": [ 1, 1, 1 ],
+ "valid": true
+ },
+ {
+ "description": "some elements match, valid minContains",
+ "data": [ 1, 2, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains = minContains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 2,
+ "minContains": 2
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, invalid maxContains",
+ "data": [ 1, 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "all elements match, valid maxContains and minContains",
+ "data": [ 1, 1 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxContains < minContains",
+ "schema": {
+ "contains": {"const": 1},
+ "maxContains": 1,
+ "minContains": 3
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": false
+ },
+ {
+ "description": "invalid minContains",
+ "data": [ 1 ],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains",
+ "data": [ 1, 1, 1 ],
+ "valid": false
+ },
+ {
+ "description": "invalid maxContains and minContains",
+ "data": [ 1, 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 0
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "minContains = 0 makes contains always pass",
+ "data": [ 2 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minContains = 0 with maxContains",
+ "schema": {
+ "contains": {"const": 1},
+ "minContains": 0,
+ "maxContains": 1
+ },
+ "tests": [
+ {
+ "description": "empty data",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "not more than maxContains",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "too many",
+ "data": [ 1, 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/minItems.json b/json/tests/draft2020-12/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft2020-12/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/minLength.json b/json/tests/draft2020-12/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft2020-12/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/minProperties.json b/json/tests/draft2020-12/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft2020-12/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/minimum.json b/json/tests/draft2020-12/minimum.json
new file mode 100644
index 0000000..21ae50e
--- /dev/null
+++ b/json/tests/draft2020-12/minimum.json
@@ -0,0 +1,69 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/multipleOf.json b/json/tests/draft2020-12/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft2020-12/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/not.json b/json/tests/draft2020-12/not.json
new file mode 100644
index 0000000..98de0ed
--- /dev/null
+++ b/json/tests/draft2020-12/not.json
@@ -0,0 +1,117 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema true",
+ "schema": {"not": true},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema false",
+ "schema": {"not": false},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/oneOf.json b/json/tests/draft2020-12/oneOf.json
new file mode 100644
index 0000000..eeb7ae8
--- /dev/null
+++ b/json/tests/draft2020-12/oneOf.json
@@ -0,0 +1,274 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all true",
+ "schema": {"oneOf": [true, true, true]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, one true",
+ "schema": {"oneOf": [true, false, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, more than one true",
+ "schema": {"oneOf": [true, true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all false",
+ "schema": {"oneOf": [false, false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": true,
+ "baz": true
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": true
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/bignum.json b/json/tests/draft2020-12/optional/bignum.json
new file mode 100644
index 0000000..3f49226
--- /dev/null
+++ b/json/tests/draft2020-12/optional/bignum.json
@@ -0,0 +1,93 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "exclusiveMaximum": 972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "exclusiveMinimum": -972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/ecmascript-regex.json b/json/tests/draft2020-12/optional/ecmascript-regex.json
new file mode 100644
index 0000000..1beb0b3
--- /dev/null
+++ b/json/tests/draft2020-12/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/float-overflow.json b/json/tests/draft2020-12/optional/float-overflow.json
new file mode 100644
index 0000000..52ff982
--- /dev/null
+++ b/json/tests/draft2020-12/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "integer", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format-assertion.json b/json/tests/draft2020-12/optional/format-assertion.json
new file mode 100644
index 0000000..0340037
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format-assertion.json
@@ -0,0 +1,42 @@
+[
+ {
+ "description": "schema that uses custom metaschema with format-assertion: false",
+ "schema": {
+ "$id": "https://schema/using/format-assertion/false",
+ "$schema": "http://localhost:1234/draft2020-12/format-assertion-false.json",
+ "format": "ipv4"
+ },
+ "tests": [
+ {
+ "description": "format-assertion: false: valid string",
+ "data": "127.0.0.1",
+ "valid": true
+ },
+ {
+ "description": "format-assertion: false: invalid string",
+ "data": "not-an-ipv4",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "schema that uses custom metaschema with format-assertion: true",
+ "schema": {
+ "$id": "https://schema/using/format-assertion/true",
+ "$schema": "http://localhost:1234/draft2020-12/format-assertion-true.json",
+ "format": "ipv4"
+ },
+ "tests": [
+ {
+ "description": "format-assertion: true: valid string",
+ "data": "127.0.0.1",
+ "valid": true
+ },
+ {
+ "description": "format-assertion: true: invalid string",
+ "data": "not-an-ipv4",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/date-time.json b/json/tests/draft2020-12/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/date.json b/json/tests/draft2020-12/optional/format/date.json
new file mode 100644
index 0000000..b4f61ee
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/date.json
@@ -0,0 +1,223 @@
+[
+ {
+ "description": "validation of date strings",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date string",
+ "data": "1963-06-19",
+ "valid": true
+ },
+ {
+ "description": "a valid date string with 31 days in January",
+ "data": "2020-01-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in January",
+ "data": "2020-01-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 28 days in February (normal)",
+ "data": "2021-02-28",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 29 days in February (normal)",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 29 days in February (leap)",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 30 days in February (leap)",
+ "data": "2020-02-30",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in March",
+ "data": "2020-03-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in March",
+ "data": "2020-03-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in April",
+ "data": "2020-04-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in April",
+ "data": "2020-04-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in May",
+ "data": "2020-05-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in May",
+ "data": "2020-05-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in June",
+ "data": "2020-06-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in June",
+ "data": "2020-06-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in July",
+ "data": "2020-07-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in July",
+ "data": "2020-07-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in August",
+ "data": "2020-08-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in August",
+ "data": "2020-08-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in September",
+ "data": "2020-09-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in September",
+ "data": "2020-09-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in October",
+ "data": "2020-10-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in October",
+ "data": "2020-10-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in November",
+ "data": "2020-11-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in November",
+ "data": "2020-11-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in December",
+ "data": "2020-12-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in December",
+ "data": "2020-12-32",
+ "valid": false
+ },
+ {
+ "description": "a invalid date string with invalid month",
+ "data": "2020-13-01",
+ "valid": false
+ },
+ {
+ "description": "an invalid date string",
+ "data": "06/19/1963",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350",
+ "valid": false
+ },
+ {
+ "description": "non-padded month dates are not valid",
+ "data": "1998-1-20",
+ "valid": false
+ },
+ {
+ "description": "non-padded day dates are not valid",
+ "data": "1998-01-1",
+ "valid": false
+ },
+ {
+ "description": "invalid month",
+ "data": "1998-13-01",
+ "valid": false
+ },
+ {
+ "description": "invalid month-day combination",
+ "data": "1998-04-31",
+ "valid": false
+ },
+ {
+ "description": "2021 is not a leap year",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "2020 is a leap year",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1963-06-1৪",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/duration.json b/json/tests/draft2020-12/optional/format/duration.json
new file mode 100644
index 0000000..741348a
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/duration.json
@@ -0,0 +1,128 @@
+[
+ {
+ "description": "validation of duration strings",
+ "schema": { "format": "duration" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid duration string",
+ "data": "P4DT12H30M5S",
+ "valid": true
+ },
+ {
+ "description": "an invalid duration string",
+ "data": "PT1D",
+ "valid": false
+ },
+ {
+ "description": "no elements present",
+ "data": "P",
+ "valid": false
+ },
+ {
+ "description": "no time elements present",
+ "data": "P1YT",
+ "valid": false
+ },
+ {
+ "description": "no date or time elements present",
+ "data": "PT",
+ "valid": false
+ },
+ {
+ "description": "elements out of order",
+ "data": "P2D1Y",
+ "valid": false
+ },
+ {
+ "description": "missing time separator",
+ "data": "P1D2H",
+ "valid": false
+ },
+ {
+ "description": "time element in the date position",
+ "data": "P2S",
+ "valid": false
+ },
+ {
+ "description": "four years duration",
+ "data": "P4Y",
+ "valid": true
+ },
+ {
+ "description": "zero time, in seconds",
+ "data": "PT0S",
+ "valid": true
+ },
+ {
+ "description": "zero time, in days",
+ "data": "P0D",
+ "valid": true
+ },
+ {
+ "description": "one month duration",
+ "data": "P1M",
+ "valid": true
+ },
+ {
+ "description": "one minute duration",
+ "data": "PT1M",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in hours",
+ "data": "PT36H",
+ "valid": true
+ },
+ {
+ "description": "one and a half days, in days and hours",
+ "data": "P1DT12H",
+ "valid": true
+ },
+ {
+ "description": "two weeks",
+ "data": "P2W",
+ "valid": true
+ },
+ {
+ "description": "weeks cannot be combined with other units",
+ "data": "P1Y2W",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "P২Y",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/email.json b/json/tests/draft2020-12/optional/format/email.json
new file mode 100644
index 0000000..5ce1c70
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/email.json
@@ -0,0 +1,118 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a space in the local part is valid",
+ "data": "\"joe bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a double dot in the local part is valid",
+ "data": "\"joe..bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "a quoted string with a @ in the local part is valid",
+ "data": "\"joe@bloggs\"@example.com",
+ "valid": true
+ },
+ {
+ "description": "an IPv4-address-literal after the @ is valid",
+ "data": "joe.bloggs@[127.0.0.1]",
+ "valid": true
+ },
+ {
+ "description": "an IPv6-address-literal after the @ is valid",
+ "data": "joe.bloggs@[IPv6:::1]",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid domain",
+ "data": "joe.bloggs@invalid=domain.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid IPv4-address-literal",
+ "data": "joe.bloggs@[127.0.0.300]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/hostname.json b/json/tests/draft2020-12/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/idn-email.json b/json/tests/draft2020-12/optional/format/idn-email.json
new file mode 100644
index 0000000..6e21374
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/idn-email.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "validation of an internationalized e-mail addresses",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid idn e-mail (example@example.test in Hangul)",
+ "data": "실례@실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "an invalid idn e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/idn-hostname.json b/json/tests/draft2020-12/optional/format/idn-hostname.json
new file mode 100644
index 0000000..6c8f86a
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/idn-hostname.json
@@ -0,0 +1,304 @@
+[
+ {
+ "description": "validation of internationalized host names",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name (example.test in Hangul)",
+ "data": "실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "illegal first char U+302E Hangul single dot tone mark",
+ "data": "〮실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "contains illegal char U+302E Hangul single dot tone mark",
+ "data": "실〮례.테스트",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "invalid label, correct Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1",
+ "data": "-> $1.00 <--",
+ "valid": false
+ },
+ {
+ "description": "valid Chinese Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4",
+ "data": "xn--ihqwcrb4cv8a8dqg056pqjye",
+ "valid": true
+ },
+ {
+ "description": "invalid Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "xn--X",
+ "valid": false
+ },
+ {
+ "description": "U-label contains \"--\" in the 3rd and 4th position",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "XN--aa---o47jg78q",
+ "valid": false
+ },
+ {
+ "description": "U-label starts with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello",
+ "valid": false
+ },
+ {
+ "description": "U-label ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "hello-",
+ "valid": false
+ },
+ {
+ "description": "U-label starts and ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello-",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Spacing Combining Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0903hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Nonspacing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0300hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with an Enclosing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0488hello",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are PVALID, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u00df\u03c2\u0f0b\u3007",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are PVALID, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u06fd\u06fe",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u0640\u07fa",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start",
+ "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no preceding 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "a\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing preceding",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no following 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7a",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing following",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with surrounding 'l's",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7l",
+ "valid": true
+ },
+ {
+ "description": "Greek KERAIA not followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375S",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA not followed by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375\u03b2",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERESH not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "A\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05d0\u05f3\u05d1",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "A\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05d0\u05f4\u05d1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "def\u30fbabc",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no other characters",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Hiragana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u3041",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Katakana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u30a1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u4e08",
+ "valid": true
+ },
+ {
+ "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0660\u06f0",
+ "valid": false
+ },
+ {
+ "description": "Arabic-Indic digits not mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0628\u0660\u0628",
+ "valid": true
+ },
+ {
+ "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9",
+ "data": "\u06f00",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u094d\u200d\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1",
+ "data": "\u0915\u094d\u200c\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement",
+ "data": "\u0628\u064a\u200c\u0628\u064a",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/ipv4.json b/json/tests/draft2020-12/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/ipv6.json b/json/tests/draft2020-12/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/iri-reference.json b/json/tests/draft2020-12/optional/format/iri-reference.json
new file mode 100644
index 0000000..c6b4c22
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/iri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of IRI References",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative IRI Reference",
+ "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid relative IRI Reference",
+ "data": "/âππ",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI Reference",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "a valid IRI Reference",
+ "data": "âππ",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI fragment",
+ "data": "#ƒrägmênt",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI fragment",
+ "data": "#ƒräg\\mênt",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/iri.json b/json/tests/draft2020-12/optional/format/iri.json
new file mode 100644
index 0000000..a0d12ae
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/iri.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of IRIs",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag and parentheses",
+ "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with URL-encoded stuff",
+ "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI based on IPv6",
+ "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI based on IPv6",
+ "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative IRI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI though valid IRI reference",
+ "data": "âππ",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/json-pointer.json b/json/tests/draft2020-12/optional/format/json-pointer.json
new file mode 100644
index 0000000..a0346b5
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/json-pointer.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of JSON-pointers (JSON String Representation)",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid JSON-pointer",
+ "data": "/foo/bar~0/baz~1/%a",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (~ not escaped)",
+ "data": "/foo/bar~",
+ "valid": false
+ },
+ {
+ "description": "valid JSON-pointer with empty segment",
+ "data": "/foo//bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer with the last empty segment",
+ "data": "/foo/bar/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #1",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #2",
+ "data": "/foo",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #3",
+ "data": "/foo/0",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #4",
+ "data": "/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #5",
+ "data": "/a~1b",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #6",
+ "data": "/c%d",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #7",
+ "data": "/e^f",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #8",
+ "data": "/g|h",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #9",
+ "data": "/i\\j",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #10",
+ "data": "/k\"l",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #11",
+ "data": "/ ",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #12",
+ "data": "/m~0n",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer used adding to the last array position",
+ "data": "/foo/-",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (- used as object member name)",
+ "data": "/foo/-/bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (multiple escaped characters)",
+ "data": "/~1~0~0~1~1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #1",
+ "data": "/~1.1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #2",
+ "data": "/~0.1",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
+ "data": "#",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
+ "data": "#/",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
+ "data": "#a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #1",
+ "data": "/~0~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #2",
+ "data": "/~0/~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #1",
+ "data": "/~2",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #2",
+ "data": "/~-1",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (multiple characters not escaped)",
+ "data": "/~~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
+ "data": "a/a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/regex.json b/json/tests/draft2020-12/optional/format/regex.json
new file mode 100644
index 0000000..3449177
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/regex.json
@@ -0,0 +1,48 @@
+[
+ {
+ "description": "validation of regular expressions",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid regular expression",
+ "data": "([abc])+\\s+$",
+ "valid": true
+ },
+ {
+ "description": "a regular expression with unclosed parens is invalid",
+ "data": "^(abc]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/relative-json-pointer.json b/json/tests/draft2020-12/optional/format/relative-json-pointer.json
new file mode 100644
index 0000000..9309986
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/relative-json-pointer.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of Relative JSON Pointers (RJP)",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid upwards RJP",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "a valid downwards RJP",
+ "data": "0/foo/bar",
+ "valid": true
+ },
+ {
+ "description": "a valid up and then down RJP, with array index",
+ "data": "2/0/baz/1/zip",
+ "valid": true
+ },
+ {
+ "description": "a valid RJP taking the member or index name",
+ "data": "0#",
+ "valid": true
+ },
+ {
+ "description": "an invalid RJP that is a valid JSON Pointer",
+ "data": "/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "negative prefix",
+ "data": "-1/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "## is not a valid json-pointer",
+ "data": "0##",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus json-pointer",
+ "data": "01/a",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus octothorpe",
+ "data": "01#",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/time.json b/json/tests/draft2020-12/optional/format/time.json
new file mode 100644
index 0000000..5011d78
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/time.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of time strings",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid time string",
+ "data": "08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with leap second, Zulu",
+ "data": "23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, zero time-offset",
+ "data": "23:59:60+00:00",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong hour)",
+ "data": "22:59:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong minute)",
+ "data": "23:58:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, positive time-offset",
+ "data": "01:29:60+01:30",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large positive time-offset",
+ "data": "23:29:60+23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong hour)",
+ "data": "23:59:60+01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong minute)",
+ "data": "23:59:60+00:30",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, negative time-offset",
+ "data": "15:59:60-08:00",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large negative time-offset",
+ "data": "00:29:60-23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong hour)",
+ "data": "23:59:60-01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong minute)",
+ "data": "23:59:60-00:30",
+ "valid": false
+ },
+ {
+ "description": "a valid time string with second fraction",
+ "data": "23:20:50.52Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with precise second fraction",
+ "data": "08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with plus offset",
+ "data": "08:30:06+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with minus offset",
+ "data": "08:30:06-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with case-insensitive Z",
+ "data": "08:30:06z",
+ "valid": true
+ },
+ {
+ "description": "an invalid time string with invalid hour",
+ "data": "24:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid minute",
+ "data": "00:60:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid second",
+ "data": "00:00:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset hour",
+ "data": "01:02:03+24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset minute",
+ "data": "01:02:03+00:60",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time with both Z and numoffset",
+ "data": "01:02:03Z+00:30",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset indicator",
+ "data": "08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "01:01:01,1111",
+ "valid": false
+ },
+ {
+ "description": "no time offset",
+ "data": "12:00:00",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/unknown.json b/json/tests/draft2020-12/optional/format/unknown.json
new file mode 100644
index 0000000..12339ae
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/unknown.json
@@ -0,0 +1,43 @@
+[
+ {
+ "description": "unknown format",
+ "schema": { "format": "unknown" },
+ "tests": [
+ {
+ "description": "unknown formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore strings",
+ "data": "string",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/uri-reference.json b/json/tests/draft2020-12/optional/format/uri-reference.json
new file mode 100644
index 0000000..7cdf228
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/uri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of URI References",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid relative URI Reference",
+ "data": "/abc",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI Reference",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "a valid URI Reference",
+ "data": "abc",
+ "valid": true
+ },
+ {
+ "description": "a valid URI fragment",
+ "data": "#fragment",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI fragment",
+ "data": "#frag\\ment",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/uri-template.json b/json/tests/draft2020-12/optional/format/uri-template.json
new file mode 100644
index 0000000..df355c5
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/uri-template.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "format: uri-template",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term}",
+ "valid": true
+ },
+ {
+ "description": "an invalid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term",
+ "valid": false
+ },
+ {
+ "description": "a valid uri-template without variables",
+ "data": "http://example.com/dictionary",
+ "valid": true
+ },
+ {
+ "description": "a valid relative uri-template",
+ "data": "dictionary/{term:1}/{term}",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/uri.json b/json/tests/draft2020-12/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/format/uuid.json b/json/tests/draft2020-12/optional/format/uuid.json
new file mode 100644
index 0000000..e54cbc0
--- /dev/null
+++ b/json/tests/draft2020-12/optional/format/uuid.json
@@ -0,0 +1,85 @@
+[
+ {
+ "description": "uuid format",
+ "schema": {
+ "format": "uuid"
+ },
+ "tests": [
+ {
+ "description": "all upper-case",
+ "data": "2EB8AA08-AA98-11EA-B4AA-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all lower-case",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d16380",
+ "valid": true
+ },
+ {
+ "description": "mixed case",
+ "data": "2eb8aa08-AA98-11ea-B4Aa-73B441D16380",
+ "valid": true
+ },
+ {
+ "description": "all zeroes is valid",
+ "data": "00000000-0000-0000-0000-000000000000",
+ "valid": true
+ },
+ {
+ "description": "wrong length",
+ "data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638",
+ "valid": false
+ },
+ {
+ "description": "missing section",
+ "data": "2eb8aa08-aa98-11ea-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "bad characters (not hex)",
+ "data": "2eb8aa08-aa98-11ea-b4ga-73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "no dashes",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too few dashes",
+ "data": "2eb8aa08aa98-11ea-b4aa73b441d16380",
+ "valid": false
+ },
+ {
+ "description": "too many dashes",
+ "data": "2eb8-aa08-aa98-11ea-b4aa73b44-1d16380",
+ "valid": false
+ },
+ {
+ "description": "dashes in the wrong spot",
+ "data": "2eb8aa08aa9811eab4aa73b441d16380----",
+ "valid": false
+ },
+ {
+ "description": "valid version 4",
+ "data": "98d80576-482e-427f-8434-7f86890ab222",
+ "valid": true
+ },
+ {
+ "description": "valid version 5",
+ "data": "99c17cbb-656f-564a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 6",
+ "data": "99c17cbb-656f-664a-940f-1a4568f03487",
+ "valid": true
+ },
+ {
+ "description": "hypothetical version 15",
+ "data": "99c17cbb-656f-f64a-940f-1a4568f03487",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/non-bmp-regex.json b/json/tests/draft2020-12/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft2020-12/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/optional/refOfUnknownKeyword.json b/json/tests/draft2020-12/optional/refOfUnknownKeyword.json
new file mode 100644
index 0000000..5b150df
--- /dev/null
+++ b/json/tests/draft2020-12/optional/refOfUnknownKeyword.json
@@ -0,0 +1,44 @@
+[
+ {
+ "description": "reference of a root arbitrary keyword ",
+ "schema": {
+ "unknown-keyword": {"type": "integer"},
+ "properties": {
+ "bar": {"$ref": "#/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "reference of an arbitrary keyword of a sub-schema",
+ "schema": {
+ "properties": {
+ "foo": {"unknown-keyword": {"type": "integer"}},
+ "bar": {"$ref": "#/properties/foo/unknown-keyword"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/pattern.json b/json/tests/draft2020-12/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft2020-12/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/patternProperties.json b/json/tests/draft2020-12/patternProperties.json
new file mode 100644
index 0000000..c10ffcc
--- /dev/null
+++ b/json/tests/draft2020-12/patternProperties.json
@@ -0,0 +1,156 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "patternProperties with boolean schemas",
+ "schema": {
+ "patternProperties": {
+ "f.*": true,
+ "b.*": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property matching schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with a property matching both true and false is invalid",
+ "data": {"foobar":1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/prefixItems.json b/json/tests/draft2020-12/prefixItems.json
new file mode 100644
index 0000000..7d0571e
--- /dev/null
+++ b/json/tests/draft2020-12/prefixItems.json
@@ -0,0 +1,81 @@
+[
+ {
+ "description": "a schema given for prefixItems",
+ "schema": {
+ "prefixItems": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "prefixItems with boolean schemas",
+ "schema": {
+ "prefixItems": [true, false]
+ },
+ "tests": [
+ {
+ "description": "array with one item is valid",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with two items is invalid",
+ "data": [ 1, "foo" ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additional items are allowed by default",
+ "schema": {"prefixItems": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/properties.json b/json/tests/draft2020-12/properties.json
new file mode 100644
index 0000000..b86c181
--- /dev/null
+++ b/json/tests/draft2020-12/properties.json
@@ -0,0 +1,167 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with boolean schema",
+ "schema": {
+ "properties": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "no property present is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "only 'true' property present is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "only 'false' property present is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "both properties present is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/propertyNames.json b/json/tests/draft2020-12/propertyNames.json
new file mode 100644
index 0000000..8423690
--- /dev/null
+++ b/json/tests/draft2020-12/propertyNames.json
@@ -0,0 +1,78 @@
+[
+ {
+ "description": "propertyNames validation",
+ "schema": {
+ "propertyNames": {"maxLength": 3}
+ },
+ "tests": [
+ {
+ "description": "all property names valid",
+ "data": {
+ "f": {},
+ "foo": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "some property names invalid",
+ "data": {
+ "foo": {},
+ "foobar": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3, 4],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema true",
+ "schema": {"propertyNames": true},
+ "tests": [
+ {
+ "description": "object with any properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema false",
+ "schema": {"propertyNames": false},
+ "tests": [
+ {
+ "description": "object with any properties is invalid",
+ "data": {"foo": 1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/ref.json b/json/tests/draft2020-12/ref.json
new file mode 100644
index 0000000..8a65e51
--- /dev/null
+++ b/json/tests/draft2020-12/ref.json
@@ -0,0 +1,581 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "prefixItems": [
+ {"type": "integer"},
+ {"$ref": "#/prefixItems/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "$defs": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/$defs/tilde~0field"},
+ "slash": {"$ref": "#/$defs/slash~1field"},
+ "percent": {"$ref": "#/$defs/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "$defs": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/$defs/a"},
+ "c": {"$ref": "#/$defs/b"}
+ },
+ "$ref": "#/$defs/c"
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref applies alongside sibling keywords",
+ "schema": {
+ "$defs": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/$defs/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid, maxItems valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems invalid",
+ "data": { "foo": [1, 2, 3] },
+ "valid": false
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {
+ "$ref": "https://json-schema.org/draft/2020-12/schema"
+ },
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/$defs/is-string"}
+ },
+ "$defs": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema true",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema false",
+ "schema": {
+ "$ref": "#/$defs/bool",
+ "$defs": {
+ "bool": false
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "$id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "$defs": {
+ "node": {
+ "$id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/$defs/foo%22bar"}
+ },
+ "$defs": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref creates new scope when adjacent to keywords",
+ "schema": {
+ "$defs": {
+ "A": {
+ "unevaluatedProperties": false
+ }
+ },
+ "properties": {
+ "prop1": {
+ "type": "string"
+ }
+ },
+ "$ref": "#/$defs/A"
+ },
+ "tests": [
+ {
+ "description": "referenced subschema doesn't see annotations from properties",
+ "data": {
+ "prop1": "match"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "$defs": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/$defs/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "do not evaluate the $ref inside the enum, definition exact match",
+ "data": { "type": "string" },
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/$defs/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "refs with relative uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-relative-uri-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "schema-relative-uri-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-relative-uri-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative refs with absolute uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs2.json",
+ "$defs": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "$ref": "#/$defs/inner"
+ }
+ },
+ "$ref": "schema-refs-absolute-uris-defs2.json"
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id must be resolved against nearest parent, not just immediate parent",
+ "schema": {
+ "$id": "http://example.com/a.json",
+ "$defs": {
+ "x": {
+ "$id": "http://example.com/b/c.json",
+ "not": {
+ "$defs": {
+ "y": {
+ "$id": "d.json",
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "http://example.com/b/d.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number should pass",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number should fail",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/refRemote.json b/json/tests/draft2020-12/refRemote.json
new file mode 100644
index 0000000..b607432
--- /dev/null
+++ b/json/tests/draft2020-12/refRemote.json
@@ -0,0 +1,190 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas-defs.json#/$defs/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "$id": "http://localhost:1234/",
+ "items": {
+ "$id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolder/"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}},
+ "$defs": {
+ "baz": {
+ "$id": "baseUriChangeFolderInSubschema/",
+ "$defs": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "$id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name-defs.json#/$defs/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref with ref to defs",
+ "schema": {
+ "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json",
+ "$ref": "ref-and-defs.json"
+ },
+ "tests": [
+ {
+ "description": "invalid",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid",
+ "data": {
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/required.json b/json/tests/draft2020-12/required.json
new file mode 100644
index 0000000..abf18f3
--- /dev/null
+++ b/json/tests/draft2020-12/required.json
@@ -0,0 +1,105 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with empty array",
+ "schema": {
+ "properties": {
+ "foo": {}
+ },
+ "required": []
+ },
+ "tests": [
+ {
+ "description": "property not required",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/type.json b/json/tests/draft2020-12/type.json
new file mode 100644
index 0000000..8304647
--- /dev/null
+++ b/json/tests/draft2020-12/type.json
@@ -0,0 +1,474 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is an integer",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number (and an integer)",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/unevaluatedItems.json b/json/tests/draft2020-12/unevaluatedItems.json
new file mode 100644
index 0000000..af209b0
--- /dev/null
+++ b/json/tests/draft2020-12/unevaluatedItems.json
@@ -0,0 +1,633 @@
+[
+ {
+ "description": "unevaluatedItems true",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems false",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems as schema",
+ "schema": {
+ "type": "array",
+ "unevaluatedItems": { "type": "string" }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated items",
+ "data": [42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with uniform items",
+ "schema": {
+ "type": "array",
+ "items": { "type": "string" },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", "bar"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with tuple",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with items",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "items": true,
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "unevaluatedItems doesn't apply",
+ "data": ["foo", 42],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested tuple",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "allOf": [
+ {
+ "prefixItems": [
+ true,
+ { "type": "number" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", 42],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", 42, true],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested items",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "items": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with nested unevaluatedItems",
+ "schema": {
+ "type": "array",
+ "allOf": [
+ {
+ "prefixItems": [
+ { "type": "string" }
+ ]
+ },
+ {
+ "unevaluatedItems": true
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no additional items",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "with additional items",
+ "data": ["foo", 42, true],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with anyOf",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "anyOf": [
+ {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "prefixItems": [
+ true,
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when one schema matches and has no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "when one schema matches and has unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ },
+ {
+ "description": "when two schemas match and has no unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "when two schemas match and has unevaluated items",
+ "data": ["foo", "bar", "baz", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with oneOf",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "oneOf": [
+ {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ {
+ "prefixItems": [
+ true,
+ { "const": "baz" }
+ ]
+ }
+ ],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with not",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "not": {
+ "not": {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ }
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with if/then/else",
+ "schema": {
+ "type": "array",
+ "prefixItems": [
+ { "const": "foo" }
+ ],
+ "if": {
+ "prefixItems": [
+ true,
+ { "const": "bar" }
+ ]
+ },
+ "then": {
+ "prefixItems": [
+ true,
+ true,
+ { "const": "then" }
+ ]
+ },
+ "else": {
+ "prefixItems": [
+ true,
+ true,
+ true,
+ { "const": "else" }
+ ]
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "when if matches and it has no unevaluated items",
+ "data": ["foo", "bar", "then"],
+ "valid": true
+ },
+ {
+ "description": "when if matches and it has unevaluated items",
+ "data": ["foo", "bar", "then", "else"],
+ "valid": false
+ },
+ {
+ "description": "when if doesn't match and it has no unevaluated items",
+ "data": ["foo", 42, 42, "else"],
+ "valid": true
+ },
+ {
+ "description": "when if doesn't match and it has unevaluated items",
+ "data": ["foo", 42, 42, "else", 42],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with boolean schemas",
+ "schema": {
+ "type": "array",
+ "allOf": [true],
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems with $ref",
+ "schema": {
+ "type": "array",
+ "$ref": "#/$defs/bar",
+ "prefixItems": [
+ { "type": "string" }
+ ],
+ "unevaluatedItems": false,
+ "$defs": {
+ "bar": {
+ "prefixItems": [
+ true,
+ { "type": "string" }
+ ]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated items",
+ "data": ["foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "with unevaluated items",
+ "data": ["foo", "bar", "baz"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "prefixItems": [ true ]
+ },
+ {
+ "unevaluatedItems": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": [ 1 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "item is evaluated in an uncle schema to unevaluatedItems",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "array",
+ "prefixItems": [
+ {
+ "type": "string"
+ }
+ ],
+ "unevaluatedItems": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "prefixItems": [
+ true,
+ {
+ "type": "string"
+ }
+ ]
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra items",
+ "data": {
+ "foo": [
+ "test"
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": [
+ "test",
+ "test"
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems depends on adjacent contains",
+ "schema": {
+ "prefixItems": [true],
+ "contains": {"type": "string"},
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "second item is evaluated by contains",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "contains fails, second item is not evaluated",
+ "data": [ 1, 2 ],
+ "valid": false
+ },
+ {
+ "description": "contains passes, second item is not evaluated",
+ "data": [ 1, 2, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems depends on multiple nested contains",
+ "schema": {
+ "allOf": [
+ { "contains": { "multipleOf": 2 } },
+ { "contains": { "multipleOf": 3 } }
+ ],
+ "unevaluatedItems": { "multipleOf": 5 }
+ },
+ "tests": [
+ {
+ "description": "5 not evaluated, passes unevaluatedItems",
+ "data": [ 2, 3, 4, 5, 6 ],
+ "valid": true
+ },
+ {
+ "description": "7 not evaluated, fails unevaluatedItems",
+ "data": [ 2, 3, 4, 7, 8 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedItems and contains interact to control item dependency relationship",
+ "schema": {
+ "if": {
+ "contains": {"const": "a"}
+ },
+ "then": {
+ "if": {
+ "contains": {"const": "b"}
+ },
+ "then": {
+ "if": {
+ "contains": {"const": "c"}
+ }
+ }
+ },
+ "unevaluatedItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "only a's are valid",
+ "data": [ "a", "a" ],
+ "valid": true
+ },
+ {
+ "description": "a's and b's are valid",
+ "data": [ "a", "b", "a", "b", "a" ],
+ "valid": true
+ },
+ {
+ "description": "a's, b's and c's are valid",
+ "data": [ "c", "a", "c", "c", "b", "a" ],
+ "valid": true
+ },
+ {
+ "description": "only b's are invalid",
+ "data": [ "b", "b" ],
+ "valid": false
+ },
+ {
+ "description": "only c's are invalid",
+ "data": [ "c", "c" ],
+ "valid": false
+ },
+ {
+ "description": "only b's and c's are invalid",
+ "data": [ "c", "b", "c", "b", "c" ],
+ "valid": false
+ },
+ {
+ "description": "only a's and c's are invalid",
+ "data": [ "c", "a", "c", "a", "c" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-array instances are valid",
+ "schema": {"unevaluatedItems": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/unevaluatedProperties.json b/json/tests/draft2020-12/unevaluatedProperties.json
new file mode 100644
index 0000000..6384cb8
--- /dev/null
+++ b/json/tests/draft2020-12/unevaluatedProperties.json
@@ -0,0 +1,1347 @@
+[
+ {
+ "description": "unevaluatedProperties true",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties schema",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": {
+ "type": "string",
+ "minLength": 3
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with valid unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with invalid unevaluated properties",
+ "data": {
+ "foo": "fo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties false",
+ "schema": {
+ "type": "object",
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent patternProperties",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with adjacent additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "additionalProperties": true,
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested properties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested patternProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "patternProperties": {
+ "^bar": { "type": "string" }
+ }
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested additionalProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "additionalProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no additional properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with additional properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with nested unevaluatedProperties",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": {
+ "type": "string",
+ "maxLength": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with anyOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ },
+ {
+ "properties": {
+ "quux": { "const": "quux" }
+ },
+ "required": ["quux"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when one matches and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when one matches and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "not-baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when two match and has no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when two match and has unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz",
+ "quux": "not-quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with oneOf",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "oneOf": [
+ {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "baz": { "const": "baz" }
+ },
+ "required": ["baz"]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "quux": "quux"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with not",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "not": {
+ "not": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, then not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "else": {
+ "properties": {
+ "baz": { "type": "string" }
+ },
+ "required": ["baz"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with if/then/else, else not defined",
+ "schema": {
+ "type": "object",
+ "if": {
+ "properties": {
+ "foo": { "const": "then" }
+ },
+ "required": ["foo"]
+ },
+ "then": {
+ "properties": {
+ "bar": { "type": "string" }
+ },
+ "required": ["bar"]
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "when if is true and has no unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "when if is true and has unevaluated properties",
+ "data": {
+ "foo": "then",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has no unevaluated properties",
+ "data": {
+ "baz": "baz"
+ },
+ "valid": false
+ },
+ {
+ "description": "when if is false and has unevaluated properties",
+ "data": {
+ "foo": "else",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with dependentSchemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "dependentSchemas": {
+ "foo": {
+ "properties": {
+ "bar": { "const": "bar" }
+ },
+ "required": ["bar"]
+ }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with boolean schemas",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [true],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties with $ref",
+ "schema": {
+ "type": "object",
+ "$ref": "#/$defs/bar",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false,
+ "$defs": {
+ "bar": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "with no unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ },
+ {
+ "description": "with unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar",
+ "baz": "baz"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties can't see inside cousins",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "always fails",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer false, inner true, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties outside",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "allOf": [
+ {
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested unevaluatedProperties, outer true, inner false, properties inside",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "unevaluatedProperties": true
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, true with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": true
+ },
+ {
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": false
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "cousin unevaluatedProperties, true and false, false with properties",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "unevaluatedProperties": true
+ },
+ {
+ "properties": {
+ "foo": { "type": "string" }
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "with no nested unevaluated properties",
+ "data": {
+ "foo": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "with nested unevaluated properties",
+ "data": {
+ "foo": "foo",
+ "bar": "bar"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property is evaluated in an uncle schema to unevaluatedProperties",
+ "comment": "see https://stackoverflow.com/questions/66936884/deeply-nested-unevaluatedproperties-and-their-expectations",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "object",
+ "properties": {
+ "bar": {
+ "type": "string"
+ }
+ },
+ "unevaluatedProperties": false
+ }
+ },
+ "anyOf": [
+ {
+ "properties": {
+ "foo": {
+ "properties": {
+ "faz": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "no extra properties",
+ "data": {
+ "foo": {
+ "bar": "test"
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "uncle keyword evaluation is not significant",
+ "data": {
+ "foo": {
+ "bar": "test",
+ "faz": "test"
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, allOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ },
+ "unevaluatedProperties": false
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "in-place applicator siblings, anyOf has unevaluated",
+ "schema": {
+ "type": "object",
+ "allOf": [
+ {
+ "properties": {
+ "foo": true
+ }
+ }
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "bar": true
+ },
+ "unevaluatedProperties": false
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "base case: both properties present",
+ "data": {
+ "foo": 1,
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, bar is missing",
+ "data": {
+ "foo": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "in place applicator siblings, foo is missing",
+ "data": {
+ "bar": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + single cyclic ref",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "x": { "$ref": "#" }
+ },
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "Single is valid",
+ "data": { "x": {} },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 1st level is invalid",
+ "data": { "x": {}, "y": {} },
+ "valid": false
+ },
+ {
+ "description": "Nested is valid",
+ "data": { "x": { "x": {} } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 2nd level is invalid",
+ "data": { "x": { "x": {}, "y": {} } },
+ "valid": false
+ },
+ {
+ "description": "Deep nested is valid",
+ "data": { "x": { "x": { "x": {} } } },
+ "valid": true
+ },
+ {
+ "description": "Unevaluated on 3rd level is invalid",
+ "data": { "x": { "x": { "x": {}, "y": {} } } },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unevaluatedProperties + ref inside allOf / oneOf",
+ "schema": {
+ "$defs": {
+ "one": {
+ "properties": { "a": true }
+ },
+ "two": {
+ "required": ["x"],
+ "properties": { "x": true }
+ }
+ },
+ "allOf": [
+ { "$ref": "#/$defs/one" },
+ { "properties": { "b": true } },
+ {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ {
+ "required": ["y"],
+ "properties": { "y": true }
+ }
+ ]
+ }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid (no x or y)",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a and b are invalid (no x or y)",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "x and y are invalid",
+ "data": { "x": 1, "y": 1 },
+ "valid": false
+ },
+ {
+ "description": "a and x are valid",
+ "data": { "a": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and y are valid",
+ "data": { "a": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x are valid",
+ "data": { "a": 1, "b": 1, "x": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and y are valid",
+ "data": { "a": 1, "b": 1, "y": 1 },
+ "valid": true
+ },
+ {
+ "description": "a and b and x and y are invalid",
+ "data": { "a": 1, "b": 1, "x": 1, "y": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dynamic evalation inside nested refs",
+ "schema": {
+ "$defs": {
+ "one": {
+ "oneOf": [
+ { "$ref": "#/$defs/two" },
+ { "required": ["b"], "properties": { "b": true } },
+ { "required": ["xx"], "patternProperties": { "x": true } },
+ { "required": ["all"], "unevaluatedProperties": true }
+ ]
+ },
+ "two": {
+ "oneOf": [
+ { "required": ["c"], "properties": { "c": true } },
+ { "required": ["d"], "properties": { "d": true } }
+ ]
+ }
+ },
+ "oneOf": [
+ { "$ref": "#/$defs/one" },
+ { "required": ["a"], "properties": { "a": true } }
+ ],
+ "unevaluatedProperties": false
+ },
+ "tests": [
+ {
+ "description": "Empty is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "a is valid",
+ "data": { "a": 1 },
+ "valid": true
+ },
+ {
+ "description": "b is valid",
+ "data": { "b": 1 },
+ "valid": true
+ },
+ {
+ "description": "c is valid",
+ "data": { "c": 1 },
+ "valid": true
+ },
+ {
+ "description": "d is valid",
+ "data": { "d": 1 },
+ "valid": true
+ },
+ {
+ "description": "a + b is invalid",
+ "data": { "a": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + c is invalid",
+ "data": { "a": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "a + d is invalid",
+ "data": { "a": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + c is invalid",
+ "data": { "b": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "b + d is invalid",
+ "data": { "b": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "c + d is invalid",
+ "data": { "c": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx is valid",
+ "data": { "xx": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foox is valid",
+ "data": { "xx": 1, "foox": 1 },
+ "valid": true
+ },
+ {
+ "description": "xx + foo is invalid",
+ "data": { "xx": 1, "foo": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + a is invalid",
+ "data": { "xx": 1, "a": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + b is invalid",
+ "data": { "xx": 1, "b": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + c is invalid",
+ "data": { "xx": 1, "c": 1 },
+ "valid": false
+ },
+ {
+ "description": "xx + d is invalid",
+ "data": { "xx": 1, "d": 1 },
+ "valid": false
+ },
+ {
+ "description": "all is valid",
+ "data": { "all": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + foo is valid",
+ "data": { "all": 1, "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "all + a is invalid",
+ "data": { "all": 1, "a": 1 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-object instances are valid",
+ "schema": {"unevaluatedProperties": false},
+ "tests": [
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/uniqueItems.json b/json/tests/draft2020-12/uniqueItems.json
new file mode 100644
index 0000000..85c619d
--- /dev/null
+++ b/json/tests/draft2020-12/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "prefixItems": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "items": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/unknownKeyword.json b/json/tests/draft2020-12/unknownKeyword.json
new file mode 100644
index 0000000..e46657d
--- /dev/null
+++ b/json/tests/draft2020-12/unknownKeyword.json
@@ -0,0 +1,56 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "$defs": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_unknown0" },
+ { "$ref": "#/$defs/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft2020-12/vocabulary.json b/json/tests/draft2020-12/vocabulary.json
new file mode 100644
index 0000000..d84f8f1
--- /dev/null
+++ b/json/tests/draft2020-12/vocabulary.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "schema that uses custom metaschema with with no validation vocabulary",
+ "schema": {
+ "$id": "https://schema/using/no/validation",
+ "$schema": "http://localhost:1234/draft2020-12/metaschema-no-validation.json",
+ "properties": {
+ "badProperty": false,
+ "numberProperty": {
+ "minimum": 10
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "applicator vocabulary still works",
+ "data": {
+ "badProperty": "this property should not exist"
+ },
+ "valid": false
+ },
+ {
+ "description": "no validation: valid number",
+ "data": {
+ "numberProperty": 20
+ },
+ "valid": true
+ },
+ {
+ "description": "no validation: invalid number, but it still validates",
+ "data": {
+ "numberProperty": 1
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/additionalItems.json b/json/tests/draft3/additionalItems.json
new file mode 100644
index 0000000..910f1d6
--- /dev/null
+++ b/json/tests/draft3/additionalItems.json
@@ -0,0 +1,113 @@
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ 1, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "when items is schema, additionalItems does nothing",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems permitted",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators",
+ "schema": {
+ "extends": [
+ { "items": [ { "type": "integer" } ] }
+ ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in extends are not examined",
+ "data": [ 1, null ],
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/additionalProperties.json b/json/tests/draft3/additionalProperties.json
new file mode 100644
index 0000000..4620618
--- /dev/null
+++ b/json/tests/draft3/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "extends": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in extends are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/default.json b/json/tests/draft3/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft3/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/dependencies.json b/json/tests/draft3/dependencies.json
new file mode 100644
index 0000000..0ffa6bf
--- /dev/null
+++ b/json/tests/draft3/dependencies.json
@@ -0,0 +1,123 @@
+[
+ {
+ "description": "dependencies",
+ "schema": {
+ "dependencies": {"bar": "foo"}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies",
+ "schema": {
+ "dependencies": {"quux": ["foo", "bar"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies subschema",
+ "schema": {
+ "dependencies": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/disallow.json b/json/tests/draft3/disallow.json
new file mode 100644
index 0000000..a5c9d90
--- /dev/null
+++ b/json/tests/draft3/disallow.json
@@ -0,0 +1,80 @@
+[
+ {
+ "description": "disallow",
+ "schema": {
+ "disallow": "integer"
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple disallow",
+ "schema": {
+ "disallow": ["integer", "boolean"]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple disallow subschema",
+ "schema": {
+ "disallow":
+ ["string",
+ {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }]
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/divisibleBy.json b/json/tests/draft3/divisibleBy.json
new file mode 100644
index 0000000..ef7cc14
--- /dev/null
+++ b/json/tests/draft3/divisibleBy.json
@@ -0,0 +1,60 @@
+[
+ {
+ "description": "by int",
+ "schema": {"divisibleBy": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"divisibleBy": 1.5},
+ "tests": [
+ {
+ "description": "zero is divisible by anything (except 0)",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is divisible by 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not divisible by 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"divisibleBy": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is divisible by 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not divisible by 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/enum.json b/json/tests/draft3/enum.json
new file mode 100644
index 0000000..5a1ab3b
--- /dev/null
+++ b/json/tests/draft3/enum.json
@@ -0,0 +1,118 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"], "required":true}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/extends.json b/json/tests/draft3/extends.json
new file mode 100644
index 0000000..909bce5
--- /dev/null
+++ b/json/tests/draft3/extends.json
@@ -0,0 +1,94 @@
+[
+ {
+ "description": "extends",
+ "schema": {
+ "properties": {"bar": {"type": "integer", "required": true}},
+ "extends": {
+ "properties": {
+ "foo": {"type": "string", "required": true}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "extends",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch extends",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch extended",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple extends",
+ "schema": {
+ "properties": {"bar": {"type": "integer", "required": true}},
+ "extends" : [
+ {
+ "properties": {
+ "foo": {"type": "string", "required": true}
+ }
+ },
+ {
+ "properties": {
+ "baz": {"type": "null", "required": true}
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch first extends",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second extends",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "extends simple types",
+ "schema": {
+ "minimum": 20,
+ "extends": {"maximum": 30}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch extends",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/format.json b/json/tests/draft3/format.json
new file mode 100644
index 0000000..a5447c9
--- /dev/null
+++ b/json/tests/draft3/format.json
@@ -0,0 +1,362 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ip-address format",
+ "schema": { "format": "ip-address" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "host-name format",
+ "schema": { "format": "host-name" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "regex format",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date format",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "time format",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "color format",
+ "schema": { "format": "color" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/infinite-loop-detection.json b/json/tests/draft3/infinite-loop-detection.json
new file mode 100644
index 0000000..090f49a
--- /dev/null
+++ b/json/tests/draft3/infinite-loop-detection.json
@@ -0,0 +1,32 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "definitions": {
+ "int": { "type": "integer" }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/int"
+ }
+ },
+ "extends": {
+ "additionalProperties": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/items.json b/json/tests/draft3/items.json
new file mode 100644
index 0000000..f5e18a1
--- /dev/null
+++ b/json/tests/draft3/items.json
@@ -0,0 +1,46 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/maxItems.json b/json/tests/draft3/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft3/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/maxLength.json b/json/tests/draft3/maxLength.json
new file mode 100644
index 0000000..4de42bc
--- /dev/null
+++ b/json/tests/draft3/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/maximum.json b/json/tests/draft3/maximum.json
new file mode 100644
index 0000000..ccb79c6
--- /dev/null
+++ b/json/tests/draft3/maximum.json
@@ -0,0 +1,99 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "maximum validation (explicit false exclusivity)",
+ "schema": {"maximum": 3.0, "exclusiveMaximum": false},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "maximum": 3.0,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "below the maximum is still valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/minItems.json b/json/tests/draft3/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft3/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/minLength.json b/json/tests/draft3/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft3/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/minimum.json b/json/tests/draft3/minimum.json
new file mode 100644
index 0000000..d579536
--- /dev/null
+++ b/json/tests/draft3/minimum.json
@@ -0,0 +1,88 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "minimum": 1.1,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "above the minimum is still valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/bignum.json b/json/tests/draft3/optional/bignum.json
new file mode 100644
index 0000000..ccc7c17
--- /dev/null
+++ b/json/tests/draft3/optional/bignum.json
@@ -0,0 +1,107 @@
+[
+ {
+ "description": "integer",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "integer",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": {"maximum": 18446744073709551615},
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "maximum": 972783798187987123879878123.18878137,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": {"minimum": -18446744073709551615},
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "minimum": -972783798187987123879878123.18878137,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/ecmascript-regex.json b/json/tests/draft3/optional/ecmascript-regex.json
new file mode 100644
index 0000000..03fe977
--- /dev/null
+++ b/json/tests/draft3/optional/ecmascript-regex.json
@@ -0,0 +1,18 @@
+[
+ {
+ "description": "ECMA 262 regex dialect recognition",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "[^] is a valid regex",
+ "data": "[^]",
+ "valid": true
+ },
+ {
+ "description": "ECMA 262 has no support for lookbehind",
+ "data": "(?<=foo)bar",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/color.json b/json/tests/draft3/optional/format/color.json
new file mode 100644
index 0000000..0c0b534
--- /dev/null
+++ b/json/tests/draft3/optional/format/color.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "validation of CSS colors",
+ "schema": { "format": "color" },
+ "tests": [
+ {
+ "description": "a valid CSS color name",
+ "data": "fuchsia",
+ "valid": true
+ },
+ {
+ "description": "a valid six-digit CSS color code",
+ "data": "#CC8899",
+ "valid": true
+ },
+ {
+ "description": "a valid three-digit CSS color code",
+ "data": "#C89",
+ "valid": true
+ },
+ {
+ "description": "an invalid CSS color code",
+ "data": "#00332520",
+ "valid": false
+ },
+ {
+ "description": "an invalid CSS color name",
+ "data": "puce",
+ "valid": false
+ },
+ {
+ "description": "a CSS color name containing invalid characters",
+ "data": "light_grayish_red-violet",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/date-time.json b/json/tests/draft3/optional/format/date-time.json
new file mode 100644
index 0000000..1f1e6fb
--- /dev/null
+++ b/json/tests/draft3/optional/format/date-time.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/date.json b/json/tests/draft3/optional/format/date.json
new file mode 100644
index 0000000..796bc46
--- /dev/null
+++ b/json/tests/draft3/optional/format/date.json
@@ -0,0 +1,168 @@
+[
+ {
+ "description": "validation of date strings",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "a valid date string",
+ "data": "1963-06-19",
+ "valid": true
+ },
+ {
+ "description": "a valid date string with 31 days in January",
+ "data": "2020-01-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in January",
+ "data": "2020-01-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 28 days in February (normal)",
+ "data": "2021-02-28",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 29 days in February (normal)",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 29 days in February (leap)",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 30 days in February (leap)",
+ "data": "2020-02-30",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in March",
+ "data": "2020-03-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in March",
+ "data": "2020-03-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in April",
+ "data": "2020-04-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in April",
+ "data": "2020-04-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in May",
+ "data": "2020-05-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in May",
+ "data": "2020-05-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in June",
+ "data": "2020-06-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in June",
+ "data": "2020-06-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in July",
+ "data": "2020-07-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in July",
+ "data": "2020-07-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in August",
+ "data": "2020-08-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in August",
+ "data": "2020-08-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in September",
+ "data": "2020-09-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in September",
+ "data": "2020-09-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in October",
+ "data": "2020-10-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in October",
+ "data": "2020-10-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in November",
+ "data": "2020-11-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in November",
+ "data": "2020-11-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in December",
+ "data": "2020-12-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in December",
+ "data": "2020-12-32",
+ "valid": false
+ },
+ {
+ "description": "a invalid date string with invalid month",
+ "data": "2020-13-01",
+ "valid": false
+ },
+ {
+ "description": "an invalid date string",
+ "data": "06/19/1963",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350",
+ "valid": false
+ },
+ {
+ "description": "invalidates non-padded month dates",
+ "data": "1998-1-20",
+ "valid": false
+ },
+ {
+ "description": "invalidates non-padded day dates",
+ "data": "1998-01-1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/email.json b/json/tests/draft3/optional/format/email.json
new file mode 100644
index 0000000..059615a
--- /dev/null
+++ b/json/tests/draft3/optional/format/email.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/host-name.json b/json/tests/draft3/optional/format/host-name.json
new file mode 100644
index 0000000..d418f37
--- /dev/null
+++ b/json/tests/draft3/optional/format/host-name.json
@@ -0,0 +1,63 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "host-name" },
+ "tests": [
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/ip-address.json b/json/tests/draft3/optional/format/ip-address.json
new file mode 100644
index 0000000..91cac9f
--- /dev/null
+++ b/json/tests/draft3/optional/format/ip-address.json
@@ -0,0 +1,23 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ip-address" },
+ "tests": [
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/ipv6.json b/json/tests/draft3/optional/format/ipv6.json
new file mode 100644
index 0000000..c3ef379
--- /dev/null
+++ b/json/tests/draft3/optional/format/ipv6.json
@@ -0,0 +1,68 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/regex.json b/json/tests/draft3/optional/format/regex.json
new file mode 100644
index 0000000..8a37763
--- /dev/null
+++ b/json/tests/draft3/optional/format/regex.json
@@ -0,0 +1,18 @@
+[
+ {
+ "description": "validation of regular expressions",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "a valid regular expression",
+ "data": "([abc])+\\s+$",
+ "valid": true
+ },
+ {
+ "description": "a regular expression with unclosed parens is invalid",
+ "data": "^(abc]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/time.json b/json/tests/draft3/optional/format/time.json
new file mode 100644
index 0000000..36c823e
--- /dev/null
+++ b/json/tests/draft3/optional/format/time.json
@@ -0,0 +1,18 @@
+[
+ {
+ "description": "validation of time strings",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "a valid time string",
+ "data": "08:30:06",
+ "valid": true
+ },
+ {
+ "description": "an invalid time string",
+ "data": "8:30 AM",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/format/uri.json b/json/tests/draft3/optional/format/uri.json
new file mode 100644
index 0000000..f024b62
--- /dev/null
+++ b/json/tests/draft3/optional/format/uri.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/non-bmp-regex.json b/json/tests/draft3/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft3/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/optional/zeroTerminatedFloats.json b/json/tests/draft3/optional/zeroTerminatedFloats.json
new file mode 100644
index 0000000..9b50ea2
--- /dev/null
+++ b/json/tests/draft3/optional/zeroTerminatedFloats.json
@@ -0,0 +1,15 @@
+[
+ {
+ "description": "some languages do not distinguish between different types of numeric value",
+ "schema": {
+ "type": "integer"
+ },
+ "tests": [
+ {
+ "description": "a float is not an integer even without fractional part",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/pattern.json b/json/tests/draft3/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft3/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/patternProperties.json b/json/tests/draft3/patternProperties.json
new file mode 100644
index 0000000..2ca9aae
--- /dev/null
+++ b/json/tests/draft3/patternProperties.json
@@ -0,0 +1,115 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/properties.json b/json/tests/draft3/properties.json
new file mode 100644
index 0000000..a830c67
--- /dev/null
+++ b/json/tests/draft3/properties.json
@@ -0,0 +1,97 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/ref.json b/json/tests/draft3/ref.json
new file mode 100644
index 0000000..924db76
--- /dev/null
+++ b/json/tests/draft3/ref.json
@@ -0,0 +1,278 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "definitions": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/definitions/tilde~0field"},
+ "slash": {"$ref": "#/definitions/slash~1field"},
+ "percent": {"$ref": "#/definitions/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "definitions": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/definitions/a"},
+ "c": {"$ref": "#/definitions/b"}
+ },
+ "$ref": "#/definitions/c"
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref overrides any sibling keywords",
+ "schema": {
+ "definitions": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "remote ref valid, maxItems ignored",
+ "data": { "foo": [ 1, 2, 3] },
+ "valid": true
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/definitions/is-string"}
+ },
+ "definitions": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref prevents a sibling id from changing the base uri",
+ "schema": {
+ "id": "http://localhost:1234/sibling_id/base/",
+ "definitions": {
+ "foo": {
+ "id": "http://localhost:1234/sibling_id/foo.json",
+ "type": "string"
+ },
+ "base_foo": {
+ "$comment": "this canonical uri is http://localhost:1234/sibling_id/base/foo.json",
+ "id": "foo.json",
+ "type": "number"
+ }
+ },
+ "allOf": [
+ {
+ "$comment": "$ref resolves to http://localhost:1234/sibling_id/base/foo.json, not http://localhost:1234/sibling_id/foo.json",
+ "id": "http://localhost:1234/sibling_id/",
+ "$ref": "foo.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "$ref resolves to /definitions/base_foo, data does not validate",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "$ref resolves to /definitions/base_foo, data validates",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {"$ref": "http://json-schema.org/draft-03/schema#"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"items": {"type": "integer"}},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"items": {"type": 1}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "definitions": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/definitions/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/definitions/a_string" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/refRemote.json b/json/tests/draft3/refRemote.json
new file mode 100644
index 0000000..de0cb43
--- /dev/null
+++ b/json/tests/draft3/refRemote.json
@@ -0,0 +1,74 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "change resolution scope",
+ "schema": {
+ "id": "http://localhost:1234/",
+ "items": {
+ "id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "changed scope ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "changed scope ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/required.json b/json/tests/draft3/required.json
new file mode 100644
index 0000000..aaaf024
--- /dev/null
+++ b/json/tests/draft3/required.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {"required" : true},
+ "bar": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required explicitly false validation",
+ "schema": {
+ "properties": {
+ "foo": {"required": false}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required if required is false",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/type.json b/json/tests/draft3/type.json
new file mode 100644
index 0000000..f962cc3
--- /dev/null
+++ b/json/tests/draft3/type.json
@@ -0,0 +1,494 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "any type matches any type",
+ "schema": {"type": "any"},
+ "tests": [
+ {
+ "description": "any type includes integers",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "any type includes float",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "any type includes string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "any type includes object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "any type includes array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "any type includes boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "any type includes null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "types can include schemas",
+ "schema": {
+ "type": [
+ "array",
+ {"type": "object"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "an integer is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "when types includes a schema it should fully validate the schema",
+ "schema": {
+ "type": [
+ "integer",
+ {
+ "properties": {
+ "foo": {"type": "null"}
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "an object is valid only if it is fully valid",
+ "data": {"foo": null},
+ "valid": true
+ },
+ {
+ "description": "an object is invalid otherwise",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "types from separate schemas are merged",
+ "schema": {
+ "type": [
+ {"type": ["string"]},
+ {"type": ["array", "null"]}
+ ]
+ },
+ "tests": [
+ {
+ "description": "an integer is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "an array is valid",
+ "data": [1, 2, 3],
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft3/uniqueItems.json b/json/tests/draft3/uniqueItems.json
new file mode 100644
index 0000000..c48c6a0
--- /dev/null
+++ b/json/tests/draft3/uniqueItems.json
@@ -0,0 +1,374 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/additionalItems.json b/json/tests/draft4/additionalItems.json
new file mode 100644
index 0000000..784bc84
--- /dev/null
+++ b/json/tests/draft4/additionalItems.json
@@ -0,0 +1,149 @@
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [{}],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ null, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ null, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "when items is schema, additionalItems does nothing",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems permitted",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" } ] }
+ ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, null ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, invalid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" }, { "type": "string" } ] }
+ ],
+ "items": [ {"type": "integer" } ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, "hello" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items validation adjusts the starting index for additionalItems",
+ "schema": {
+ "items": [ { "type": "string" } ],
+ "additionalItems": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/additionalProperties.json b/json/tests/draft4/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft4/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/allOf.json b/json/tests/draft4/allOf.json
new file mode 100644
index 0000000..fc7dec5
--- /dev/null
+++ b/json/tests/draft4/allOf.json
@@ -0,0 +1,261 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/anyOf.json b/json/tests/draft4/anyOf.json
new file mode 100644
index 0000000..f8d82e8
--- /dev/null
+++ b/json/tests/draft4/anyOf.json
@@ -0,0 +1,182 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/default.json b/json/tests/draft4/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft4/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/definitions.json b/json/tests/draft4/definitions.json
new file mode 100644
index 0000000..482823b
--- /dev/null
+++ b/json/tests/draft4/definitions.json
@@ -0,0 +1,26 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": "integer"}
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": 1}
+ }
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/dependencies.json b/json/tests/draft4/dependencies.json
new file mode 100644
index 0000000..51eeddf
--- /dev/null
+++ b/json/tests/draft4/dependencies.json
@@ -0,0 +1,194 @@
+[
+ {
+ "description": "dependencies",
+ "schema": {
+ "dependencies": {"bar": ["foo"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies",
+ "schema": {
+ "dependencies": {"quux": ["foo", "bar"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies subschema",
+ "schema": {
+ "dependencies": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependencies": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\tbar": {
+ "minProperties": 4
+ },
+ "foo'bar": {"required": ["foo\"bar"]},
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "valid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 3",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 3",
+ "data": {
+ "foo'bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 4",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/enum.json b/json/tests/draft4/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft4/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/format.json b/json/tests/draft4/format.json
new file mode 100644
index 0000000..5bd83cc
--- /dev/null
+++ b/json/tests/draft4/format.json
@@ -0,0 +1,218 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/id.json b/json/tests/draft4/id.json
new file mode 100644
index 0000000..1c91d33
--- /dev/null
+++ b/json/tests/draft4/id.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an id buried in the enum",
+ "schema": {
+ "definitions": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "id": "https://localhost:1234/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "id": "https://localhost:1234/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "id": "https://localhost:1234/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/definitions/id_in_enum" },
+ { "$ref": "https://localhost:1234/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "id": "https://localhost:1234/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to id",
+ "data": "a string to match #/definitions/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+
+]
diff --git a/json/tests/draft4/infinite-loop-detection.json b/json/tests/draft4/infinite-loop-detection.json
new file mode 100644
index 0000000..f98c74f
--- /dev/null
+++ b/json/tests/draft4/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "definitions": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/items.json b/json/tests/draft4/items.json
new file mode 100644
index 0000000..7bf9f02
--- /dev/null
+++ b/json/tests/draft4/items.json
@@ -0,0 +1,195 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "definitions": {
+ "item": {
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/sub-item" },
+ { "$ref": "#/definitions/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/maxItems.json b/json/tests/draft4/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft4/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/maxLength.json b/json/tests/draft4/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft4/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/maxProperties.json b/json/tests/draft4/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft4/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/maximum.json b/json/tests/draft4/maximum.json
new file mode 100644
index 0000000..ccb79c6
--- /dev/null
+++ b/json/tests/draft4/maximum.json
@@ -0,0 +1,99 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "maximum validation (explicit false exclusivity)",
+ "schema": {"maximum": 3.0, "exclusiveMaximum": false},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "maximum": 3.0,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "below the maximum is still valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/minItems.json b/json/tests/draft4/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft4/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/minLength.json b/json/tests/draft4/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft4/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/minProperties.json b/json/tests/draft4/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft4/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/minimum.json b/json/tests/draft4/minimum.json
new file mode 100644
index 0000000..22d310e
--- /dev/null
+++ b/json/tests/draft4/minimum.json
@@ -0,0 +1,114 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation (explicit false exclusivity)",
+ "schema": {"minimum": 1.1, "exclusiveMinimum": false},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "minimum": 1.1,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "above the minimum is still valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/multipleOf.json b/json/tests/draft4/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft4/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/not.json b/json/tests/draft4/not.json
new file mode 100644
index 0000000..cbb7f46
--- /dev/null
+++ b/json/tests/draft4/not.json
@@ -0,0 +1,96 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ }
+
+]
diff --git a/json/tests/draft4/oneOf.json b/json/tests/draft4/oneOf.json
new file mode 100644
index 0000000..fb63b08
--- /dev/null
+++ b/json/tests/draft4/oneOf.json
@@ -0,0 +1,230 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {},
+ "baz": {}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/bignum.json b/json/tests/draft4/optional/bignum.json
new file mode 100644
index 0000000..7a622de
--- /dev/null
+++ b/json/tests/draft4/optional/bignum.json
@@ -0,0 +1,95 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "maximum": 972783798187987123879878123.18878137,
+ "exclusiveMaximum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "minimum": -972783798187987123879878123.18878137,
+ "exclusiveMinimum": true
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/ecmascript-regex.json b/json/tests/draft4/optional/ecmascript-regex.json
new file mode 100644
index 0000000..77624cf
--- /dev/null
+++ b/json/tests/draft4/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": {}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": {}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": {}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": {}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": {}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/float-overflow.json b/json/tests/draft4/optional/float-overflow.json
new file mode 100644
index 0000000..47fd5ba
--- /dev/null
+++ b/json/tests/draft4/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "number", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/date-time.json b/json/tests/draft4/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft4/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/email.json b/json/tests/draft4/optional/format/email.json
new file mode 100644
index 0000000..d6761a4
--- /dev/null
+++ b/json/tests/draft4/optional/format/email.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/hostname.json b/json/tests/draft4/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft4/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/ipv4.json b/json/tests/draft4/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft4/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/ipv6.json b/json/tests/draft4/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft4/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/unknown.json b/json/tests/draft4/optional/format/unknown.json
new file mode 100644
index 0000000..12339ae
--- /dev/null
+++ b/json/tests/draft4/optional/format/unknown.json
@@ -0,0 +1,43 @@
+[
+ {
+ "description": "unknown format",
+ "schema": { "format": "unknown" },
+ "tests": [
+ {
+ "description": "unknown formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore strings",
+ "data": "string",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/format/uri.json b/json/tests/draft4/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft4/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/non-bmp-regex.json b/json/tests/draft4/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft4/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/optional/zeroTerminatedFloats.json b/json/tests/draft4/optional/zeroTerminatedFloats.json
new file mode 100644
index 0000000..9b50ea2
--- /dev/null
+++ b/json/tests/draft4/optional/zeroTerminatedFloats.json
@@ -0,0 +1,15 @@
+[
+ {
+ "description": "some languages do not distinguish between different types of numeric value",
+ "schema": {
+ "type": "integer"
+ },
+ "tests": [
+ {
+ "description": "a float is not an integer even without fractional part",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/pattern.json b/json/tests/draft4/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft4/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/patternProperties.json b/json/tests/draft4/patternProperties.json
new file mode 100644
index 0000000..5f741df
--- /dev/null
+++ b/json/tests/draft4/patternProperties.json
@@ -0,0 +1,120 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/properties.json b/json/tests/draft4/properties.json
new file mode 100644
index 0000000..688527b
--- /dev/null
+++ b/json/tests/draft4/properties.json
@@ -0,0 +1,136 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/ref.json b/json/tests/draft4/ref.json
new file mode 100644
index 0000000..d9978e9
--- /dev/null
+++ b/json/tests/draft4/ref.json
@@ -0,0 +1,507 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "definitions": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/definitions/tilde~0field"},
+ "slash": {"$ref": "#/definitions/slash~1field"},
+ "percent": {"$ref": "#/definitions/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "definitions": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/definitions/a"},
+ "c": {"$ref": "#/definitions/b"}
+ },
+ "allOf": [{ "$ref": "#/definitions/c" }]
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref overrides any sibling keywords",
+ "schema": {
+ "definitions": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems ignored",
+ "data": { "foo": [ 1, 2, 3] },
+ "valid": true
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref prevents a sibling id from changing the base uri",
+ "schema": {
+ "id": "http://localhost:1234/sibling_id/base/",
+ "definitions": {
+ "foo": {
+ "id": "http://localhost:1234/sibling_id/foo.json",
+ "type": "string"
+ },
+ "base_foo": {
+ "$comment": "this canonical uri is http://localhost:1234/sibling_id/base/foo.json",
+ "id": "foo.json",
+ "type": "number"
+ }
+ },
+ "allOf": [
+ {
+ "$comment": "$ref resolves to http://localhost:1234/sibling_id/base/foo.json, not http://localhost:1234/sibling_id/foo.json",
+ "id": "http://localhost:1234/sibling_id/",
+ "$ref": "foo.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "$ref resolves to /definitions/base_foo, data does not validate",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "$ref resolves to /definitions/base_foo, data validates",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {"$ref": "http://json-schema.org/draft-04/schema#"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/definitions/is-string"}
+ },
+ "definitions": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "definitions": {
+ "node": {
+ "id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/definitions/foo%22bar"}
+ },
+ "definitions": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "allOf": [{
+ "$ref": "#foo"
+ }],
+ "definitions": {
+ "A": {
+ "id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "id": "http://localhost:1234/root",
+ "allOf": [{
+ "$ref": "http://localhost:1234/nested.json#foo"
+ }],
+ "definitions": {
+ "A": {
+ "id": "nested.json",
+ "definitions": {
+ "B": {
+ "id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "definitions": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/definitions/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/definitions/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "id must be resolved against nearest parent, not just immediate parent",
+ "schema": {
+ "id": "http://example.com/a.json",
+ "definitions": {
+ "x": {
+ "id": "http://example.com/b/c.json",
+ "not": {
+ "definitions": {
+ "y": {
+ "id": "d.json",
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "http://example.com/b/d.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number should pass",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number should fail",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/refRemote.json b/json/tests/draft4/refRemote.json
new file mode 100644
index 0000000..ce5e99a
--- /dev/null
+++ b/json/tests/draft4/refRemote.json
@@ -0,0 +1,171 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "id": "http://localhost:1234/",
+ "items": {
+ "id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz"}
+ },
+ "definitions": {
+ "baz": {
+ "id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz/definitions/bar"}
+ },
+ "definitions": {
+ "baz": {
+ "id": "baseUriChangeFolderInSubschema/",
+ "definitions": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name.json#/definitions/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/required.json b/json/tests/draft4/required.json
new file mode 100644
index 0000000..9b05318
--- /dev/null
+++ b/json/tests/draft4/required.json
@@ -0,0 +1,89 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/type.json b/json/tests/draft4/type.json
new file mode 100644
index 0000000..df46677
--- /dev/null
+++ b/json/tests/draft4/type.json
@@ -0,0 +1,469 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft4/uniqueItems.json b/json/tests/draft4/uniqueItems.json
new file mode 100644
index 0000000..2ccf666
--- /dev/null
+++ b/json/tests/draft4/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/additionalItems.json b/json/tests/draft6/additionalItems.json
new file mode 100644
index 0000000..784bc84
--- /dev/null
+++ b/json/tests/draft6/additionalItems.json
@@ -0,0 +1,149 @@
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [{}],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ null, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ null, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "when items is schema, additionalItems does nothing",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems permitted",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" } ] }
+ ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, null ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, invalid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" }, { "type": "string" } ] }
+ ],
+ "items": [ {"type": "integer" } ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, "hello" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items validation adjusts the starting index for additionalItems",
+ "schema": {
+ "items": [ { "type": "string" } ],
+ "additionalItems": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/additionalProperties.json b/json/tests/draft6/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft6/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/allOf.json b/json/tests/draft6/allOf.json
new file mode 100644
index 0000000..ec9319e
--- /dev/null
+++ b/json/tests/draft6/allOf.json
@@ -0,0 +1,294 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all true",
+ "schema": {"allOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, some false",
+ "schema": {"allOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all false",
+ "schema": {"allOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/anyOf.json b/json/tests/draft6/anyOf.json
new file mode 100644
index 0000000..b720afa
--- /dev/null
+++ b/json/tests/draft6/anyOf.json
@@ -0,0 +1,215 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all true",
+ "schema": {"anyOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, some true",
+ "schema": {"anyOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all false",
+ "schema": {"anyOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/boolean_schema.json b/json/tests/draft6/boolean_schema.json
new file mode 100644
index 0000000..6d40f23
--- /dev/null
+++ b/json/tests/draft6/boolean_schema.json
@@ -0,0 +1,104 @@
+[
+ {
+ "description": "boolean schema 'true'",
+ "schema": true,
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "boolean true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "boolean false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean schema 'false'",
+ "schema": false,
+ "tests": [
+ {
+ "description": "number is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "boolean true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "boolean false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/const.json b/json/tests/draft6/const.json
new file mode 100644
index 0000000..1c2cafc
--- /dev/null
+++ b/json/tests/draft6/const.json
@@ -0,0 +1,342 @@
+[
+ {
+ "description": "const validation",
+ "schema": {"const": 2},
+ "tests": [
+ {
+ "description": "same value is valid",
+ "data": 2,
+ "valid": true
+ },
+ {
+ "description": "another value is invalid",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with object",
+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
+ "tests": [
+ {
+ "description": "same object is valid",
+ "data": {"foo": "bar", "baz": "bax"},
+ "valid": true
+ },
+ {
+ "description": "same object with different property order is valid",
+ "data": {"baz": "bax", "foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "another object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": [1, 2],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with array",
+ "schema": {"const": [{ "foo": "bar" }]},
+ "tests": [
+ {
+ "description": "same array is valid",
+ "data": [{"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "another array item is invalid",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "array with additional items is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with null",
+ "schema": {"const": null},
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "not null is invalid",
+ "data": 0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with false does not match 0",
+ "schema": {"const": false},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with true does not match 1",
+ "schema": {"const": true},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [false] does not match [0]",
+ "schema": {"const": [false]},
+ "tests": [
+ {
+ "description": "[false] is valid",
+ "data": [false],
+ "valid": true
+ },
+ {
+ "description": "[0] is invalid",
+ "data": [0],
+ "valid": false
+ },
+ {
+ "description": "[0.0] is invalid",
+ "data": [0.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [true] does not match [1]",
+ "schema": {"const": [true]},
+ "tests": [
+ {
+ "description": "[true] is valid",
+ "data": [true],
+ "valid": true
+ },
+ {
+ "description": "[1] is invalid",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "[1.0] is invalid",
+ "data": [1.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": false} does not match {\"a\": 0}",
+ "schema": {"const": {"a": false}},
+ "tests": [
+ {
+ "description": "{\"a\": false} is valid",
+ "data": {"a": false},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 0} is invalid",
+ "data": {"a": 0},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 0.0} is invalid",
+ "data": {"a": 0.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": true} does not match {\"a\": 1}",
+ "schema": {"const": {"a": true}},
+ "tests": [
+ {
+ "description": "{\"a\": true} is valid",
+ "data": {"a": true},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 1} is invalid",
+ "data": {"a": 1},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 1.0} is invalid",
+ "data": {"a": 1.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 0 does not match other zero-like types",
+ "schema": {"const": 0},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "empty string is invalid",
+ "data": "",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 1 does not match true",
+ "schema": {"const": 1},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "const with -2.0 matches integer and float types",
+ "schema": {"const": -2.0},
+ "tests": [
+ {
+ "description": "integer -2 is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "integer 2 is invalid",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "float -2.0 is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float 2.0 is invalid",
+ "data": 2.0,
+ "valid": false
+ },
+ {
+ "description": "float -2.00001 is invalid",
+ "data": -2.00001,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "float and integers are equal up to 64-bit representation limits",
+ "schema": {"const": 9007199254740992},
+ "tests": [
+ {
+ "description": "integer is valid",
+ "data": 9007199254740992,
+ "valid": true
+ },
+ {
+ "description": "integer minus one is invalid",
+ "data": 9007199254740991,
+ "valid": false
+ },
+ {
+ "description": "float is valid",
+ "data": 9007199254740992.0,
+ "valid": true
+ },
+ {
+ "description": "float minus one is invalid",
+ "data": 9007199254740991.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "const": "hello\u0000there" },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/contains.json b/json/tests/draft6/contains.json
new file mode 100644
index 0000000..c5471cc
--- /dev/null
+++ b/json/tests/draft6/contains.json
@@ -0,0 +1,129 @@
+[
+ {
+ "description": "contains keyword validation",
+ "schema": {
+ "contains": {"minimum": 5}
+ },
+ "tests": [
+ {
+ "description": "array with item matching schema (5) is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with item matching schema (6) is valid",
+ "data": [3, 4, 6],
+ "valid": true
+ },
+ {
+ "description": "array with two items matching schema (5, 6) is valid",
+ "data": [3, 4, 5, 6],
+ "valid": true
+ },
+ {
+ "description": "array without items matching schema is invalid",
+ "data": [2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "not array is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with const keyword",
+ "schema": {
+ "contains": { "const": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item 5 is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with two items 5 is valid",
+ "data": [3, 4, 5, 5],
+ "valid": true
+ },
+ {
+ "description": "array without item 5 is invalid",
+ "data": [1, 2, 3, 4],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema true",
+ "schema": {"contains": true},
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema false",
+ "schema": {"contains": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "non-arrays are valid",
+ "data": "contains does not apply to strings",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items + contains",
+ "schema": {
+ "items": { "multipleOf": 2 },
+ "contains": { "multipleOf": 3 }
+ },
+ "tests": [
+ {
+ "description": "matches items, does not match contains",
+ "data": [ 2, 4, 8 ],
+ "valid": false
+ },
+ {
+ "description": "does not match items, matches contains",
+ "data": [ 3, 6, 9 ],
+ "valid": false
+ },
+ {
+ "description": "matches both items and contains",
+ "data": [ 6, 12 ],
+ "valid": true
+ },
+ {
+ "description": "matches neither items nor contains",
+ "data": [ 1, 5 ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/default.json b/json/tests/draft6/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft6/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/definitions.json b/json/tests/draft6/definitions.json
new file mode 100644
index 0000000..d772fde
--- /dev/null
+++ b/json/tests/draft6/definitions.json
@@ -0,0 +1,26 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {"$ref": "http://json-schema.org/draft-06/schema#"},
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": "integer"}
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": 1}
+ }
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/dependencies.json b/json/tests/draft6/dependencies.json
new file mode 100644
index 0000000..a5e5428
--- /dev/null
+++ b/json/tests/draft6/dependencies.json
@@ -0,0 +1,248 @@
+[
+ {
+ "description": "dependencies",
+ "schema": {
+ "dependencies": {"bar": ["foo"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with empty array",
+ "schema": {
+ "dependencies": {"bar": []}
+ },
+ "tests": [
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "object with one property",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "non-object is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies",
+ "schema": {
+ "dependencies": {"quux": ["foo", "bar"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies subschema",
+ "schema": {
+ "dependencies": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with boolean subschemas",
+ "schema": {
+ "dependencies": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property having schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property having schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependencies": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\tbar": {
+ "minProperties": 4
+ },
+ "foo'bar": {"required": ["foo\"bar"]},
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "valid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 3",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 3",
+ "data": {
+ "foo'bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 4",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/enum.json b/json/tests/draft6/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft6/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/exclusiveMaximum.json b/json/tests/draft6/exclusiveMaximum.json
new file mode 100644
index 0000000..dc3cd70
--- /dev/null
+++ b/json/tests/draft6/exclusiveMaximum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "exclusiveMaximum": 3.0
+ },
+ "tests": [
+ {
+ "description": "below the exclusiveMaximum is valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ },
+ {
+ "description": "above the exclusiveMaximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/exclusiveMinimum.json b/json/tests/draft6/exclusiveMinimum.json
new file mode 100644
index 0000000..b38d7ec
--- /dev/null
+++ b/json/tests/draft6/exclusiveMinimum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "exclusiveMinimum": 1.1
+ },
+ "tests": [
+ {
+ "description": "above the exclusiveMinimum is valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "below the exclusiveMinimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/format.json b/json/tests/draft6/format.json
new file mode 100644
index 0000000..2df2a9f
--- /dev/null
+++ b/json/tests/draft6/format.json
@@ -0,0 +1,326 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "json-pointer format",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-reference format",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-template format",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/id.json b/json/tests/draft6/id.json
new file mode 100644
index 0000000..b58e0d0
--- /dev/null
+++ b/json/tests/draft6/id.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an id buried in the enum",
+ "schema": {
+ "definitions": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/definitions/id_in_enum" },
+ { "$ref": "https://localhost:1234/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to id",
+ "data": "a string to match #/definitions/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+
+]
diff --git a/json/tests/draft6/infinite-loop-detection.json b/json/tests/draft6/infinite-loop-detection.json
new file mode 100644
index 0000000..f98c74f
--- /dev/null
+++ b/json/tests/draft6/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "definitions": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/items.json b/json/tests/draft6/items.json
new file mode 100644
index 0000000..67f1184
--- /dev/null
+++ b/json/tests/draft6/items.json
@@ -0,0 +1,250 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (true)",
+ "schema": {"items": true},
+ "tests": [
+ {
+ "description": "any array is valid",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (false)",
+ "schema": {"items": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": [ 1, "foo", true ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schemas",
+ "schema": {
+ "items": [true, false]
+ },
+ "tests": [
+ {
+ "description": "array with one item is valid",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with two items is invalid",
+ "data": [ 1, "foo" ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "definitions": {
+ "item": {
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/sub-item" },
+ { "$ref": "#/definitions/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/maxItems.json b/json/tests/draft6/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft6/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/maxLength.json b/json/tests/draft6/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft6/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/maxProperties.json b/json/tests/draft6/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft6/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/maximum.json b/json/tests/draft6/maximum.json
new file mode 100644
index 0000000..6844a39
--- /dev/null
+++ b/json/tests/draft6/maximum.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/minItems.json b/json/tests/draft6/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft6/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/minLength.json b/json/tests/draft6/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft6/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/minProperties.json b/json/tests/draft6/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft6/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/minimum.json b/json/tests/draft6/minimum.json
new file mode 100644
index 0000000..21ae50e
--- /dev/null
+++ b/json/tests/draft6/minimum.json
@@ -0,0 +1,69 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/multipleOf.json b/json/tests/draft6/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft6/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/not.json b/json/tests/draft6/not.json
new file mode 100644
index 0000000..98de0ed
--- /dev/null
+++ b/json/tests/draft6/not.json
@@ -0,0 +1,117 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema true",
+ "schema": {"not": true},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema false",
+ "schema": {"not": false},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/oneOf.json b/json/tests/draft6/oneOf.json
new file mode 100644
index 0000000..eeb7ae8
--- /dev/null
+++ b/json/tests/draft6/oneOf.json
@@ -0,0 +1,274 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all true",
+ "schema": {"oneOf": [true, true, true]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, one true",
+ "schema": {"oneOf": [true, false, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, more than one true",
+ "schema": {"oneOf": [true, true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all false",
+ "schema": {"oneOf": [false, false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": true,
+ "baz": true
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": true
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/bignum.json b/json/tests/draft6/optional/bignum.json
new file mode 100644
index 0000000..3f49226
--- /dev/null
+++ b/json/tests/draft6/optional/bignum.json
@@ -0,0 +1,93 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "exclusiveMaximum": 972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "exclusiveMinimum": -972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/ecmascript-regex.json b/json/tests/draft6/optional/ecmascript-regex.json
new file mode 100644
index 0000000..1beb0b3
--- /dev/null
+++ b/json/tests/draft6/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/float-overflow.json b/json/tests/draft6/optional/float-overflow.json
new file mode 100644
index 0000000..52ff982
--- /dev/null
+++ b/json/tests/draft6/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "integer", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/date-time.json b/json/tests/draft6/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft6/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/email.json b/json/tests/draft6/optional/format/email.json
new file mode 100644
index 0000000..d6761a4
--- /dev/null
+++ b/json/tests/draft6/optional/format/email.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/hostname.json b/json/tests/draft6/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft6/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/ipv4.json b/json/tests/draft6/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft6/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/ipv6.json b/json/tests/draft6/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft6/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/json-pointer.json b/json/tests/draft6/optional/format/json-pointer.json
new file mode 100644
index 0000000..a0346b5
--- /dev/null
+++ b/json/tests/draft6/optional/format/json-pointer.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of JSON-pointers (JSON String Representation)",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid JSON-pointer",
+ "data": "/foo/bar~0/baz~1/%a",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (~ not escaped)",
+ "data": "/foo/bar~",
+ "valid": false
+ },
+ {
+ "description": "valid JSON-pointer with empty segment",
+ "data": "/foo//bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer with the last empty segment",
+ "data": "/foo/bar/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #1",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #2",
+ "data": "/foo",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #3",
+ "data": "/foo/0",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #4",
+ "data": "/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #5",
+ "data": "/a~1b",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #6",
+ "data": "/c%d",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #7",
+ "data": "/e^f",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #8",
+ "data": "/g|h",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #9",
+ "data": "/i\\j",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #10",
+ "data": "/k\"l",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #11",
+ "data": "/ ",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #12",
+ "data": "/m~0n",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer used adding to the last array position",
+ "data": "/foo/-",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (- used as object member name)",
+ "data": "/foo/-/bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (multiple escaped characters)",
+ "data": "/~1~0~0~1~1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #1",
+ "data": "/~1.1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #2",
+ "data": "/~0.1",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
+ "data": "#",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
+ "data": "#/",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
+ "data": "#a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #1",
+ "data": "/~0~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #2",
+ "data": "/~0/~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #1",
+ "data": "/~2",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #2",
+ "data": "/~-1",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (multiple characters not escaped)",
+ "data": "/~~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
+ "data": "a/a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/unknown.json b/json/tests/draft6/optional/format/unknown.json
new file mode 100644
index 0000000..12339ae
--- /dev/null
+++ b/json/tests/draft6/optional/format/unknown.json
@@ -0,0 +1,43 @@
+[
+ {
+ "description": "unknown format",
+ "schema": { "format": "unknown" },
+ "tests": [
+ {
+ "description": "unknown formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore strings",
+ "data": "string",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/uri-reference.json b/json/tests/draft6/optional/format/uri-reference.json
new file mode 100644
index 0000000..7cdf228
--- /dev/null
+++ b/json/tests/draft6/optional/format/uri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of URI References",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid relative URI Reference",
+ "data": "/abc",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI Reference",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "a valid URI Reference",
+ "data": "abc",
+ "valid": true
+ },
+ {
+ "description": "a valid URI fragment",
+ "data": "#fragment",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI fragment",
+ "data": "#frag\\ment",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/uri-template.json b/json/tests/draft6/optional/format/uri-template.json
new file mode 100644
index 0000000..df355c5
--- /dev/null
+++ b/json/tests/draft6/optional/format/uri-template.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "format: uri-template",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term}",
+ "valid": true
+ },
+ {
+ "description": "an invalid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term",
+ "valid": false
+ },
+ {
+ "description": "a valid uri-template without variables",
+ "data": "http://example.com/dictionary",
+ "valid": true
+ },
+ {
+ "description": "a valid relative uri-template",
+ "data": "dictionary/{term:1}/{term}",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/format/uri.json b/json/tests/draft6/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft6/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/optional/non-bmp-regex.json b/json/tests/draft6/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft6/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/pattern.json b/json/tests/draft6/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft6/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/patternProperties.json b/json/tests/draft6/patternProperties.json
new file mode 100644
index 0000000..c10ffcc
--- /dev/null
+++ b/json/tests/draft6/patternProperties.json
@@ -0,0 +1,156 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "patternProperties with boolean schemas",
+ "schema": {
+ "patternProperties": {
+ "f.*": true,
+ "b.*": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property matching schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with a property matching both true and false is invalid",
+ "data": {"foobar":1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/properties.json b/json/tests/draft6/properties.json
new file mode 100644
index 0000000..b86c181
--- /dev/null
+++ b/json/tests/draft6/properties.json
@@ -0,0 +1,167 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with boolean schema",
+ "schema": {
+ "properties": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "no property present is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "only 'true' property present is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "only 'false' property present is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "both properties present is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/propertyNames.json b/json/tests/draft6/propertyNames.json
new file mode 100644
index 0000000..f0788e6
--- /dev/null
+++ b/json/tests/draft6/propertyNames.json
@@ -0,0 +1,107 @@
+[
+ {
+ "description": "propertyNames validation",
+ "schema": {
+ "propertyNames": {"maxLength": 3}
+ },
+ "tests": [
+ {
+ "description": "all property names valid",
+ "data": {
+ "f": {},
+ "foo": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "some property names invalid",
+ "data": {
+ "foo": {},
+ "foobar": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3, 4],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames validation with pattern",
+ "schema": {
+ "propertyNames": { "pattern": "^a+$" }
+ },
+ "tests": [
+ {
+ "description": "matching property names valid",
+ "data": {
+ "a": {},
+ "aa": {},
+ "aaa": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "non-matching property name is invalid",
+ "data": {
+ "aaA": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema true",
+ "schema": {"propertyNames": true},
+ "tests": [
+ {
+ "description": "object with any properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema false",
+ "schema": {"propertyNames": false},
+ "tests": [
+ {
+ "description": "object with any properties is invalid",
+ "data": {"foo": 1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/ref.json b/json/tests/draft6/ref.json
new file mode 100644
index 0000000..ce5caf6
--- /dev/null
+++ b/json/tests/draft6/ref.json
@@ -0,0 +1,612 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "definitions": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/definitions/tilde~0field"},
+ "slash": {"$ref": "#/definitions/slash~1field"},
+ "percent": {"$ref": "#/definitions/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "definitions": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/definitions/a"},
+ "c": {"$ref": "#/definitions/b"}
+ },
+ "allOf": [{ "$ref": "#/definitions/c" }]
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref overrides any sibling keywords",
+ "schema": {
+ "definitions": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems ignored",
+ "data": { "foo": [ 1, 2, 3] },
+ "valid": true
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref prevents a sibling $id from changing the base uri",
+ "schema": {
+ "$id": "http://localhost:1234/sibling_id/base/",
+ "definitions": {
+ "foo": {
+ "$id": "http://localhost:1234/sibling_id/foo.json",
+ "type": "string"
+ },
+ "base_foo": {
+ "$comment": "this canonical uri is http://localhost:1234/sibling_id/base/foo.json",
+ "$id": "foo.json",
+ "type": "number"
+ }
+ },
+ "allOf": [
+ {
+ "$comment": "$ref resolves to http://localhost:1234/sibling_id/base/foo.json, not http://localhost:1234/sibling_id/foo.json",
+ "$id": "http://localhost:1234/sibling_id/",
+ "$ref": "foo.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "$ref resolves to /definitions/base_foo, data does not validate",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "$ref resolves to /definitions/base_foo, data validates",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {"$ref": "http://json-schema.org/draft-06/schema#"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/definitions/is-string"}
+ },
+ "definitions": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema true",
+ "schema": {
+ "allOf": [{ "$ref": "#/definitions/bool" }],
+ "definitions": {
+ "bool": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema false",
+ "schema": {
+ "allOf": [{ "$ref": "#/definitions/bool" }],
+ "definitions": {
+ "bool": false
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "$id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "definitions": {
+ "node": {
+ "$id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/definitions/foo%22bar"}
+ },
+ "definitions": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "allOf": [{
+ "$ref": "#foo"
+ }],
+ "definitions": {
+ "A": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/root",
+ "allOf": [{
+ "$ref": "http://localhost:1234/nested.json#foo"
+ }],
+ "definitions": {
+ "A": {
+ "$id": "nested.json",
+ "definitions": {
+ "B": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "definitions": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/definitions/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "do not evaluate the $ref inside the enum, definition exact match",
+ "data": { "type": "string" },
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/definitions/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "refs with relative uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-relative-uri-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "schema-relative-uri-defs2.json",
+ "definitions": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "allOf": [ { "$ref": "#/definitions/inner" } ]
+ }
+ },
+ "allOf": [ { "$ref": "schema-relative-uri-defs2.json" } ]
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative refs with absolute uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs2.json",
+ "definitions": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "allOf": [ { "$ref": "#/definitions/inner" } ]
+ }
+ },
+ "allOf": [ { "$ref": "schema-refs-absolute-uris-defs2.json" } ]
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/refRemote.json b/json/tests/draft6/refRemote.json
new file mode 100644
index 0000000..a2221b2
--- /dev/null
+++ b/json/tests/draft6/refRemote.json
@@ -0,0 +1,196 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "$id": "http://localhost:1234/",
+ "items": {
+ "$id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz"}
+ },
+ "definitions": {
+ "baz": {
+ "$id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz/definitions/bar"}
+ },
+ "definitions": {
+ "baz": {
+ "$id": "baseUriChangeFolderInSubschema/",
+ "definitions": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "$id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name.json#/definitions/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref with ref to definitions",
+ "schema": {
+ "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json",
+ "allOf": [
+ { "$ref": "ref-and-definitions.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "invalid",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid",
+ "data": {
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/required.json b/json/tests/draft6/required.json
new file mode 100644
index 0000000..abf18f3
--- /dev/null
+++ b/json/tests/draft6/required.json
@@ -0,0 +1,105 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with empty array",
+ "schema": {
+ "properties": {
+ "foo": {}
+ },
+ "required": []
+ },
+ "tests": [
+ {
+ "description": "property not required",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/type.json b/json/tests/draft6/type.json
new file mode 100644
index 0000000..8304647
--- /dev/null
+++ b/json/tests/draft6/type.json
@@ -0,0 +1,474 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is an integer",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number (and an integer)",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/uniqueItems.json b/json/tests/draft6/uniqueItems.json
new file mode 100644
index 0000000..2ccf666
--- /dev/null
+++ b/json/tests/draft6/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft6/unknownKeyword.json b/json/tests/draft6/unknownKeyword.json
new file mode 100644
index 0000000..1f58d97
--- /dev/null
+++ b/json/tests/draft6/unknownKeyword.json
@@ -0,0 +1,56 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "definitions": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/definitions/id_in_unknown0" },
+ { "$ref": "#/definitions/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/additionalItems.json b/json/tests/draft7/additionalItems.json
new file mode 100644
index 0000000..784bc84
--- /dev/null
+++ b/json/tests/draft7/additionalItems.json
@@ -0,0 +1,149 @@
+[
+ {
+ "description": "additionalItems as schema",
+ "schema": {
+ "items": [{}],
+ "additionalItems": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "additional items match schema",
+ "data": [ null, 2, 3, 4 ],
+ "valid": true
+ },
+ {
+ "description": "additional items do not match schema",
+ "data": [ null, 2, 3, "foo" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "when items is schema, additionalItems does nothing",
+ "schema": {
+ "items": {},
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "all items match schema",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "array of items with no additionalItems permitted",
+ "schema": {
+ "items": [{}, {}, {}],
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (1)",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "fewer number of items present (2)",
+ "data": [ 1, 2 ],
+ "valid": true
+ },
+ {
+ "description": "equal number of items present",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "additional items are not permitted",
+ "data": [ 1, 2, 3, 4 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalItems as false without items",
+ "schema": {"additionalItems": false},
+ "tests": [
+ {
+ "description":
+ "items defaults to empty schema so everything is valid",
+ "data": [ 1, 2, 3, 4, 5 ],
+ "valid": true
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems are allowed by default",
+ "schema": {"items": [{"type": "integer"}]},
+ "tests": [
+ {
+ "description": "only the first item is validated",
+ "data": [1, "foo", false],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, valid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" } ] }
+ ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, null ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalItems should not look in applicators, invalid case",
+ "schema": {
+ "allOf": [
+ { "items": [ { "type": "integer" }, { "type": "string" } ] }
+ ],
+ "items": [ {"type": "integer" } ],
+ "additionalItems": { "type": "boolean" }
+ },
+ "tests": [
+ {
+ "description": "items defined in allOf are not examined",
+ "data": [ 1, "hello" ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "items validation adjusts the starting index for additionalItems",
+ "schema": {
+ "items": [ { "type": "string" } ],
+ "additionalItems": { "type": "integer" }
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ "x", 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of second item",
+ "data": [ "x", "y" ],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/additionalProperties.json b/json/tests/draft7/additionalProperties.json
new file mode 100644
index 0000000..381275a
--- /dev/null
+++ b/json/tests/draft7/additionalProperties.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description":
+ "additionalProperties being false does not allow other properties",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "patternProperties": { "^v": {} },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : "boom"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobarbaz",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "patternProperties are not additional properties",
+ "data": {"foo":1, "vroom": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "non-ASCII pattern with additionalProperties",
+ "schema": {
+ "patternProperties": {"^á": {}},
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "matching the pattern is valid",
+ "data": {"ármányos": 2},
+ "valid": true
+ },
+ {
+ "description": "not matching the pattern is invalid",
+ "data": {"élmény": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties allows a schema which should validate",
+ "schema": {
+ "properties": {"foo": {}, "bar": {}},
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "no additional properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1, "bar" : 2, "quux" : 12},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description":
+ "additionalProperties can exist by itself",
+ "schema": {
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "an additional valid property is valid",
+ "data": {"foo" : true},
+ "valid": true
+ },
+ {
+ "description": "an additional invalid property is invalid",
+ "data": {"foo" : 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties are allowed by default",
+ "schema": {"properties": {"foo": {}, "bar": {}}},
+ "tests": [
+ {
+ "description": "additional properties are allowed",
+ "data": {"foo": 1, "bar": 2, "quux": true},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "additionalProperties should not look in applicators",
+ "schema": {
+ "allOf": [
+ {"properties": {"foo": {}}}
+ ],
+ "additionalProperties": {"type": "boolean"}
+ },
+ "tests": [
+ {
+ "description": "properties defined in allOf are not examined",
+ "data": {"foo": 1, "bar": true},
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/allOf.json b/json/tests/draft7/allOf.json
new file mode 100644
index 0000000..ec9319e
--- /dev/null
+++ b/json/tests/draft7/allOf.json
@@ -0,0 +1,294 @@
+[
+ {
+ "description": "allOf",
+ "schema": {
+ "allOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "allOf",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "mismatch second",
+ "data": {"foo": "baz"},
+ "valid": false
+ },
+ {
+ "description": "mismatch first",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "baz", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with base schema",
+ "schema": {
+ "properties": {"bar": {"type": "integer"}},
+ "required": ["bar"],
+ "allOf" : [
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ },
+ {
+ "properties": {
+ "baz": {"type": "null"}
+ },
+ "required": ["baz"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": "quux", "bar": 2, "baz": null},
+ "valid": true
+ },
+ {
+ "description": "mismatch base schema",
+ "data": {"foo": "quux", "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch first allOf",
+ "data": {"bar": 2, "baz": null},
+ "valid": false
+ },
+ {
+ "description": "mismatch second allOf",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "mismatch both",
+ "data": {"bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf simple types",
+ "schema": {
+ "allOf": [
+ {"maximum": 30},
+ {"minimum": 20}
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": 25,
+ "valid": true
+ },
+ {
+ "description": "mismatch one",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all true",
+ "schema": {"allOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, some false",
+ "schema": {"allOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with boolean schemas, all false",
+ "schema": {"allOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with one empty schema",
+ "schema": {
+ "allOf": [
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with two empty schemas",
+ "schema": {
+ "allOf": [
+ {},
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "any data is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "allOf with the first empty schema",
+ "schema": {
+ "allOf": [
+ {},
+ { "type": "number" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf with the last empty schema",
+ "schema": {
+ "allOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested allOf, to check validation semantics",
+ "schema": {
+ "allOf": [
+ {
+ "allOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "allOf combined with anyOf, oneOf",
+ "schema": {
+ "allOf": [ { "multipleOf": 2 } ],
+ "anyOf": [ { "multipleOf": 3 } ],
+ "oneOf": [ { "multipleOf": 5 } ]
+ },
+ "tests": [
+ {
+ "description": "allOf: false, anyOf: false, oneOf: false",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: false, oneOf: true",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: false",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "allOf: false, anyOf: true, oneOf: true",
+ "data": 15,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: false",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: false, oneOf: true",
+ "data": 10,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: false",
+ "data": 6,
+ "valid": false
+ },
+ {
+ "description": "allOf: true, anyOf: true, oneOf: true",
+ "data": 30,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/anyOf.json b/json/tests/draft7/anyOf.json
new file mode 100644
index 0000000..b720afa
--- /dev/null
+++ b/json/tests/draft7/anyOf.json
@@ -0,0 +1,215 @@
+[
+ {
+ "description": "anyOf",
+ "schema": {
+ "anyOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid",
+ "data": 3,
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with base schema",
+ "schema": {
+ "type": "string",
+ "anyOf" : [
+ {
+ "maxLength": 2
+ },
+ {
+ "minLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one anyOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both anyOf invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all true",
+ "schema": {"anyOf": [true, true]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, some true",
+ "schema": {"anyOf": [true, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "anyOf with boolean schemas, all false",
+ "schema": {"anyOf": [false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf complex types",
+ "schema": {
+ "anyOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first anyOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second anyOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both anyOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "neither anyOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "anyOf with one empty schema",
+ "schema": {
+ "anyOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 123,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested anyOf, to check validation semantics",
+ "schema": {
+ "anyOf": [
+ {
+ "anyOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/boolean_schema.json b/json/tests/draft7/boolean_schema.json
new file mode 100644
index 0000000..6d40f23
--- /dev/null
+++ b/json/tests/draft7/boolean_schema.json
@@ -0,0 +1,104 @@
+[
+ {
+ "description": "boolean schema 'true'",
+ "schema": true,
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "boolean true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "boolean false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "boolean schema 'false'",
+ "schema": false,
+ "tests": [
+ {
+ "description": "number is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "boolean true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "boolean false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/const.json b/json/tests/draft7/const.json
new file mode 100644
index 0000000..1c2cafc
--- /dev/null
+++ b/json/tests/draft7/const.json
@@ -0,0 +1,342 @@
+[
+ {
+ "description": "const validation",
+ "schema": {"const": 2},
+ "tests": [
+ {
+ "description": "same value is valid",
+ "data": 2,
+ "valid": true
+ },
+ {
+ "description": "another value is invalid",
+ "data": 5,
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with object",
+ "schema": {"const": {"foo": "bar", "baz": "bax"}},
+ "tests": [
+ {
+ "description": "same object is valid",
+ "data": {"foo": "bar", "baz": "bax"},
+ "valid": true
+ },
+ {
+ "description": "same object with different property order is valid",
+ "data": {"baz": "bax", "foo": "bar"},
+ "valid": true
+ },
+ {
+ "description": "another object is invalid",
+ "data": {"foo": "bar"},
+ "valid": false
+ },
+ {
+ "description": "another type is invalid",
+ "data": [1, 2],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with array",
+ "schema": {"const": [{ "foo": "bar" }]},
+ "tests": [
+ {
+ "description": "same array is valid",
+ "data": [{"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "another array item is invalid",
+ "data": [2],
+ "valid": false
+ },
+ {
+ "description": "array with additional items is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with null",
+ "schema": {"const": null},
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "not null is invalid",
+ "data": 0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with false does not match 0",
+ "schema": {"const": false},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with true does not match 1",
+ "schema": {"const": true},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [false] does not match [0]",
+ "schema": {"const": [false]},
+ "tests": [
+ {
+ "description": "[false] is valid",
+ "data": [false],
+ "valid": true
+ },
+ {
+ "description": "[0] is invalid",
+ "data": [0],
+ "valid": false
+ },
+ {
+ "description": "[0.0] is invalid",
+ "data": [0.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with [true] does not match [1]",
+ "schema": {"const": [true]},
+ "tests": [
+ {
+ "description": "[true] is valid",
+ "data": [true],
+ "valid": true
+ },
+ {
+ "description": "[1] is invalid",
+ "data": [1],
+ "valid": false
+ },
+ {
+ "description": "[1.0] is invalid",
+ "data": [1.0],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": false} does not match {\"a\": 0}",
+ "schema": {"const": {"a": false}},
+ "tests": [
+ {
+ "description": "{\"a\": false} is valid",
+ "data": {"a": false},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 0} is invalid",
+ "data": {"a": 0},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 0.0} is invalid",
+ "data": {"a": 0.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with {\"a\": true} does not match {\"a\": 1}",
+ "schema": {"const": {"a": true}},
+ "tests": [
+ {
+ "description": "{\"a\": true} is valid",
+ "data": {"a": true},
+ "valid": true
+ },
+ {
+ "description": "{\"a\": 1} is invalid",
+ "data": {"a": 1},
+ "valid": false
+ },
+ {
+ "description": "{\"a\": 1.0} is invalid",
+ "data": {"a": 1.0},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 0 does not match other zero-like types",
+ "schema": {"const": 0},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ },
+ {
+ "description": "empty object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "empty string is invalid",
+ "data": "",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "const with 1 does not match true",
+ "schema": {"const": 1},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "const with -2.0 matches integer and float types",
+ "schema": {"const": -2.0},
+ "tests": [
+ {
+ "description": "integer -2 is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "integer 2 is invalid",
+ "data": 2,
+ "valid": false
+ },
+ {
+ "description": "float -2.0 is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float 2.0 is invalid",
+ "data": 2.0,
+ "valid": false
+ },
+ {
+ "description": "float -2.00001 is invalid",
+ "data": -2.00001,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "float and integers are equal up to 64-bit representation limits",
+ "schema": {"const": 9007199254740992},
+ "tests": [
+ {
+ "description": "integer is valid",
+ "data": 9007199254740992,
+ "valid": true
+ },
+ {
+ "description": "integer minus one is invalid",
+ "data": 9007199254740991,
+ "valid": false
+ },
+ {
+ "description": "float is valid",
+ "data": 9007199254740992.0,
+ "valid": true
+ },
+ {
+ "description": "float minus one is invalid",
+ "data": 9007199254740991.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "const": "hello\u0000there" },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/contains.json b/json/tests/draft7/contains.json
new file mode 100644
index 0000000..215da98
--- /dev/null
+++ b/json/tests/draft7/contains.json
@@ -0,0 +1,150 @@
+[
+ {
+ "description": "contains keyword validation",
+ "schema": {
+ "contains": {"minimum": 5}
+ },
+ "tests": [
+ {
+ "description": "array with item matching schema (5) is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with item matching schema (6) is valid",
+ "data": [3, 4, 6],
+ "valid": true
+ },
+ {
+ "description": "array with two items matching schema (5, 6) is valid",
+ "data": [3, 4, 5, 6],
+ "valid": true
+ },
+ {
+ "description": "array without items matching schema is invalid",
+ "data": [2, 3, 4],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "not array is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with const keyword",
+ "schema": {
+ "contains": { "const": 5 }
+ },
+ "tests": [
+ {
+ "description": "array with item 5 is valid",
+ "data": [3, 4, 5],
+ "valid": true
+ },
+ {
+ "description": "array with two items 5 is valid",
+ "data": [3, 4, 5, 5],
+ "valid": true
+ },
+ {
+ "description": "array without item 5 is invalid",
+ "data": [1, 2, 3, 4],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema true",
+ "schema": {"contains": true},
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains keyword with boolean schema false",
+ "schema": {"contains": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": ["foo"],
+ "valid": false
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "non-arrays are valid",
+ "data": "contains does not apply to strings",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items + contains",
+ "schema": {
+ "items": { "multipleOf": 2 },
+ "contains": { "multipleOf": 3 }
+ },
+ "tests": [
+ {
+ "description": "matches items, does not match contains",
+ "data": [ 2, 4, 8 ],
+ "valid": false
+ },
+ {
+ "description": "does not match items, matches contains",
+ "data": [ 3, 6, 9 ],
+ "valid": false
+ },
+ {
+ "description": "matches both items and contains",
+ "data": [ 6, 12 ],
+ "valid": true
+ },
+ {
+ "description": "matches neither items nor contains",
+ "data": [ 1, 5 ],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "contains with false if subschema",
+ "schema": {
+ "contains": {
+ "if": false,
+ "else": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any non-empty array is valid",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "empty array is invalid",
+ "data": [],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/default.json b/json/tests/draft7/default.json
new file mode 100644
index 0000000..289a9b6
--- /dev/null
+++ b/json/tests/draft7/default.json
@@ -0,0 +1,79 @@
+[
+ {
+ "description": "invalid type for default",
+ "schema": {
+ "properties": {
+ "foo": {
+ "type": "integer",
+ "default": []
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"foo": 13},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "invalid string value for default",
+ "schema": {
+ "properties": {
+ "bar": {
+ "type": "string",
+ "minLength": 4,
+ "default": "bad"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when property is specified",
+ "data": {"bar": "good"},
+ "valid": true
+ },
+ {
+ "description": "still valid when the invalid default is used",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "the default keyword does not do anything if the property is missing",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "alpha": {
+ "type": "number",
+ "maximum": 3,
+ "default": 5
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "an explicit property value is checked against maximum (passing)",
+ "data": { "alpha": 1 },
+ "valid": true
+ },
+ {
+ "description": "an explicit property value is checked against maximum (failing)",
+ "data": { "alpha": 5 },
+ "valid": false
+ },
+ {
+ "description": "missing properties are not filled in with the default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/definitions.json b/json/tests/draft7/definitions.json
new file mode 100644
index 0000000..afe396e
--- /dev/null
+++ b/json/tests/draft7/definitions.json
@@ -0,0 +1,26 @@
+[
+ {
+ "description": "validate definition against metaschema",
+ "schema": {"$ref": "http://json-schema.org/draft-07/schema#"},
+ "tests": [
+ {
+ "description": "valid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": "integer"}
+ }
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid definition schema",
+ "data": {
+ "definitions": {
+ "foo": {"type": 1}
+ }
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/dependencies.json b/json/tests/draft7/dependencies.json
new file mode 100644
index 0000000..a5e5428
--- /dev/null
+++ b/json/tests/draft7/dependencies.json
@@ -0,0 +1,248 @@
+[
+ {
+ "description": "dependencies",
+ "schema": {
+ "dependencies": {"bar": ["foo"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependant",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "with dependency",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["bar"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with empty array",
+ "schema": {
+ "dependencies": {"bar": []}
+ },
+ "tests": [
+ {
+ "description": "empty object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "object with one property",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "non-object is valid",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies",
+ "schema": {
+ "dependencies": {"quux": ["foo", "bar"]}
+ },
+ "tests": [
+ {
+ "description": "neither",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "nondependants",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "with dependencies",
+ "data": {"foo": 1, "bar": 2, "quux": 3},
+ "valid": true
+ },
+ {
+ "description": "missing dependency",
+ "data": {"foo": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing other dependency",
+ "data": {"bar": 1, "quux": 2},
+ "valid": false
+ },
+ {
+ "description": "missing both dependencies",
+ "data": {"quux": 1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "multiple dependencies subschema",
+ "schema": {
+ "dependencies": {
+ "bar": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "integer"}
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "no dependency",
+ "data": {"foo": "quux"},
+ "valid": true
+ },
+ {
+ "description": "wrong type",
+ "data": {"foo": "quux", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "wrong type other",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ },
+ {
+ "description": "wrong type both",
+ "data": {"foo": "quux", "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "dependencies with boolean subschemas",
+ "schema": {
+ "dependencies": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property having schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property having schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "dependencies with escaped characters",
+ "schema": {
+ "dependencies": {
+ "foo\nbar": ["foo\rbar"],
+ "foo\tbar": {
+ "minProperties": 4
+ },
+ "foo'bar": {"required": ["foo\"bar"]},
+ "foo\"bar": ["foo'bar"]
+ }
+ },
+ "tests": [
+ {
+ "description": "valid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo\rbar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2,
+ "b": 3,
+ "c": 4
+ },
+ "valid": true
+ },
+ {
+ "description": "valid object 3",
+ "data": {
+ "foo'bar": 1,
+ "foo\"bar": 2
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid object 1",
+ "data": {
+ "foo\nbar": 1,
+ "foo": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 2",
+ "data": {
+ "foo\tbar": 1,
+ "a": 2
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 3",
+ "data": {
+ "foo'bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid object 4",
+ "data": {
+ "foo\"bar": 2
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/enum.json b/json/tests/draft7/enum.json
new file mode 100644
index 0000000..f085097
--- /dev/null
+++ b/json/tests/draft7/enum.json
@@ -0,0 +1,236 @@
+[
+ {
+ "description": "simple enum validation",
+ "schema": {"enum": [1, 2, 3]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": 4,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum validation",
+ "schema": {"enum": [6, "foo", [], true, {"foo": 12}]},
+ "tests": [
+ {
+ "description": "one of the enum is valid",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "objects are deep compared",
+ "data": {"foo": false},
+ "valid": false
+ },
+ {
+ "description": "valid object matches",
+ "data": {"foo": 12},
+ "valid": true
+ },
+ {
+ "description": "extra properties in object is invalid",
+ "data": {"foo": 12, "boo": 42},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "heterogeneous enum-with-null validation",
+ "schema": { "enum": [6, null] },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is valid",
+ "data": 6,
+ "valid": true
+ },
+ {
+ "description": "something else is invalid",
+ "data": "test",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enums in properties",
+ "schema": {
+ "type":"object",
+ "properties": {
+ "foo": {"enum":["foo"]},
+ "bar": {"enum":["bar"]}
+ },
+ "required": ["bar"]
+ },
+ "tests": [
+ {
+ "description": "both properties are valid",
+ "data": {"foo":"foo", "bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "wrong foo value",
+ "data": {"foo":"foot", "bar":"bar"},
+ "valid": false
+ },
+ {
+ "description": "wrong bar value",
+ "data": {"foo":"foo", "bar":"bart"},
+ "valid": false
+ },
+ {
+ "description": "missing optional property is valid",
+ "data": {"bar":"bar"},
+ "valid": true
+ },
+ {
+ "description": "missing required property is invalid",
+ "data": {"foo":"foo"},
+ "valid": false
+ },
+ {
+ "description": "missing all properties is invalid",
+ "data": {},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with escaped characters",
+ "schema": {
+ "enum": ["foo\nbar", "foo\rbar"]
+ },
+ "tests": [
+ {
+ "description": "member 1 is valid",
+ "data": "foo\nbar",
+ "valid": true
+ },
+ {
+ "description": "member 2 is valid",
+ "data": "foo\rbar",
+ "valid": true
+ },
+ {
+ "description": "another string is invalid",
+ "data": "abc",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with false does not match 0",
+ "schema": {"enum": [false]},
+ "tests": [
+ {
+ "description": "false is valid",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "integer zero is invalid",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "float zero is invalid",
+ "data": 0.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with true does not match 1",
+ "schema": {"enum": [true]},
+ "tests": [
+ {
+ "description": "true is valid",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "integer one is invalid",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "float one is invalid",
+ "data": 1.0,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "enum with 0 does not match false",
+ "schema": {"enum": [0]},
+ "tests": [
+ {
+ "description": "false is invalid",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "integer zero is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "float zero is valid",
+ "data": 0.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "enum with 1 does not match true",
+ "schema": {"enum": [1]},
+ "tests": [
+ {
+ "description": "true is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "integer one is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "float one is valid",
+ "data": 1.0,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nul characters in strings",
+ "schema": { "enum": [ "hello\u0000there" ] },
+ "tests": [
+ {
+ "description": "match string with nul",
+ "data": "hello\u0000there",
+ "valid": true
+ },
+ {
+ "description": "do not match string lacking nul",
+ "data": "hellothere",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/exclusiveMaximum.json b/json/tests/draft7/exclusiveMaximum.json
new file mode 100644
index 0000000..dc3cd70
--- /dev/null
+++ b/json/tests/draft7/exclusiveMaximum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMaximum validation",
+ "schema": {
+ "exclusiveMaximum": 3.0
+ },
+ "tests": [
+ {
+ "description": "below the exclusiveMaximum is valid",
+ "data": 2.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 3.0,
+ "valid": false
+ },
+ {
+ "description": "above the exclusiveMaximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/exclusiveMinimum.json b/json/tests/draft7/exclusiveMinimum.json
new file mode 100644
index 0000000..b38d7ec
--- /dev/null
+++ b/json/tests/draft7/exclusiveMinimum.json
@@ -0,0 +1,30 @@
+[
+ {
+ "description": "exclusiveMinimum validation",
+ "schema": {
+ "exclusiveMinimum": 1.1
+ },
+ "tests": [
+ {
+ "description": "above the exclusiveMinimum is valid",
+ "data": 1.2,
+ "valid": true
+ },
+ {
+ "description": "boundary point is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "below the exclusiveMinimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/format.json b/json/tests/draft7/format.json
new file mode 100644
index 0000000..e2447d6
--- /dev/null
+++ b/json/tests/draft7/format.json
@@ -0,0 +1,614 @@
+[
+ {
+ "description": "email format",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-email format",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "regex format",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv4 format",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ipv6 format",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "idn-hostname format",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "hostname format",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date format",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "date-time format",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "time format",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "json-pointer format",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative-json-pointer format",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri format",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "iri-reference format",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri format",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-reference format",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uri-template format",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/id.json b/json/tests/draft7/id.json
new file mode 100644
index 0000000..b58e0d0
--- /dev/null
+++ b/json/tests/draft7/id.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an id buried in the enum",
+ "schema": {
+ "definitions": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/definitions/id_in_enum" },
+ { "$ref": "https://localhost:1234/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to id",
+ "data": "a string to match #/definitions/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+
+]
diff --git a/json/tests/draft7/if-then-else.json b/json/tests/draft7/if-then-else.json
new file mode 100644
index 0000000..284e919
--- /dev/null
+++ b/json/tests/draft7/if-then-else.json
@@ -0,0 +1,258 @@
+[
+ {
+ "description": "ignore if without then or else",
+ "schema": {
+ "if": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone if",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone if",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore then without if",
+ "schema": {
+ "then": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone then",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone then",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ignore else without if",
+ "schema": {
+ "else": {
+ "const": 0
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when valid against lone else",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "valid when invalid against lone else",
+ "data": "hello",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and then without else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid when if test fails",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if and else without then",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid when if test passes",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "validate against correct branch, then vs else",
+ "schema": {
+ "if": {
+ "exclusiveMaximum": 0
+ },
+ "then": {
+ "minimum": -10
+ },
+ "else": {
+ "multipleOf": 2
+ }
+ },
+ "tests": [
+ {
+ "description": "valid through then",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "invalid through then",
+ "data": -100,
+ "valid": false
+ },
+ {
+ "description": "valid through else",
+ "data": 4,
+ "valid": true
+ },
+ {
+ "description": "invalid through else",
+ "data": 3,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "non-interference across combined schemas",
+ "schema": {
+ "allOf": [
+ {
+ "if": {
+ "exclusiveMaximum": 0
+ }
+ },
+ {
+ "then": {
+ "minimum": -10
+ }
+ },
+ {
+ "else": {
+ "multipleOf": 2
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid, but would have been invalid through then",
+ "data": -100,
+ "valid": true
+ },
+ {
+ "description": "valid, but would have been invalid through else",
+ "data": 3,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema true",
+ "schema": {
+ "if": true,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema true in if always chooses the then path (valid)",
+ "data": "then",
+ "valid": true
+ },
+ {
+ "description": "boolean schema true in if always chooses the then path (invalid)",
+ "data": "else",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "if with boolean schema false",
+ "schema": {
+ "if": false,
+ "then": { "const": "then" },
+ "else": { "const": "else" }
+ },
+ "tests": [
+ {
+ "description": "boolean schema false in if always chooses the else path (invalid)",
+ "data": "then",
+ "valid": false
+ },
+ {
+ "description": "boolean schema false in if always chooses the else path (valid)",
+ "data": "else",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "if appears at the end when serialized (keyword processing sequence)",
+ "schema": {
+ "then": { "const": "yes" },
+ "else": { "const": "other" },
+ "if": { "maxLength": 4 }
+ },
+ "tests": [
+ {
+ "description": "yes redirects to then and passes",
+ "data": "yes",
+ "valid": true
+ },
+ {
+ "description": "other redirects to else and passes",
+ "data": "other",
+ "valid": true
+ },
+ {
+ "description": "no redirects to then and fails",
+ "data": "no",
+ "valid": false
+ },
+ {
+ "description": "invalid redirects to else and fails",
+ "data": "invalid",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/infinite-loop-detection.json b/json/tests/draft7/infinite-loop-detection.json
new file mode 100644
index 0000000..f98c74f
--- /dev/null
+++ b/json/tests/draft7/infinite-loop-detection.json
@@ -0,0 +1,36 @@
+[
+ {
+ "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop",
+ "schema": {
+ "definitions": {
+ "int": { "type": "integer" }
+ },
+ "allOf": [
+ {
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ },
+ {
+ "additionalProperties": {
+ "$ref": "#/definitions/int"
+ }
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "passing case",
+ "data": { "foo": 1 },
+ "valid": true
+ },
+ {
+ "description": "failing case",
+ "data": { "foo": "a string" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/items.json b/json/tests/draft7/items.json
new file mode 100644
index 0000000..67f1184
--- /dev/null
+++ b/json/tests/draft7/items.json
@@ -0,0 +1,250 @@
+[
+ {
+ "description": "a schema given for items",
+ "schema": {
+ "items": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [ 1, 2, 3 ],
+ "valid": true
+ },
+ {
+ "description": "wrong type of items",
+ "data": [1, "x"],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": {"foo" : "bar"},
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "length": 1
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "an array of schemas for items",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"type": "string"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "correct types",
+ "data": [ 1, "foo" ],
+ "valid": true
+ },
+ {
+ "description": "wrong types",
+ "data": [ "foo", 1 ],
+ "valid": false
+ },
+ {
+ "description": "incomplete array of items",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with additional items",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array",
+ "data": [ ],
+ "valid": true
+ },
+ {
+ "description": "JavaScript pseudo-array is valid",
+ "data": {
+ "0": "invalid",
+ "1": "valid",
+ "length": 2
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (true)",
+ "schema": {"items": true},
+ "tests": [
+ {
+ "description": "any array is valid",
+ "data": [ 1, "foo", true ],
+ "valid": true
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schema (false)",
+ "schema": {"items": false},
+ "tests": [
+ {
+ "description": "any non-empty array is invalid",
+ "data": [ 1, "foo", true ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items with boolean schemas",
+ "schema": {
+ "items": [true, false]
+ },
+ "tests": [
+ {
+ "description": "array with one item is valid",
+ "data": [ 1 ],
+ "valid": true
+ },
+ {
+ "description": "array with two items is invalid",
+ "data": [ 1, "foo" ],
+ "valid": false
+ },
+ {
+ "description": "empty array is valid",
+ "data": [],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "items and subitems",
+ "schema": {
+ "definitions": {
+ "item": {
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/sub-item" },
+ { "$ref": "#/definitions/sub-item" }
+ ]
+ },
+ "sub-item": {
+ "type": "object",
+ "required": ["foo"]
+ }
+ },
+ "type": "array",
+ "additionalItems": false,
+ "items": [
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" },
+ { "$ref": "#/definitions/item" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "valid items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": true
+ },
+ {
+ "description": "too many items",
+ "data": [
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "too many sub-items",
+ "data": [
+ [ {"foo": null}, {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong item",
+ "data": [
+ {"foo": null},
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "wrong sub-item",
+ "data": [
+ [ {}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ],
+ [ {"foo": null}, {"foo": null} ]
+ ],
+ "valid": false
+ },
+ {
+ "description": "fewer items is valid",
+ "data": [
+ [ {"foo": null} ],
+ [ {"foo": null} ]
+ ],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested items",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid nested array",
+ "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": true
+ },
+ {
+ "description": "nested array with invalid type",
+ "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]],
+ "valid": false
+ },
+ {
+ "description": "not deep enough",
+ "data": [[[1], [2],[3]], [[4], [5], [6]]],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/maxItems.json b/json/tests/draft7/maxItems.json
new file mode 100644
index 0000000..3b53a6b
--- /dev/null
+++ b/json/tests/draft7/maxItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "maxItems validation",
+ "schema": {"maxItems": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": [1, 2, 3],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "foobar",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/maxLength.json b/json/tests/draft7/maxLength.json
new file mode 100644
index 0000000..811d35b
--- /dev/null
+++ b/json/tests/draft7/maxLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "maxLength validation",
+ "schema": {"maxLength": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": "f",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ },
+ {
+ "description": "two supplementary Unicode code points is long enough",
+ "data": "\uD83D\uDCA9\uD83D\uDCA9",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/maxProperties.json b/json/tests/draft7/maxProperties.json
new file mode 100644
index 0000000..aa7209f
--- /dev/null
+++ b/json/tests/draft7/maxProperties.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maxProperties validation",
+ "schema": {"maxProperties": 2},
+ "tests": [
+ {
+ "description": "shorter is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "too long is invalid",
+ "data": {"foo": 1, "bar": 2, "baz": 3},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maxProperties = 0 means the object is empty",
+ "schema": { "maxProperties": 0 },
+ "tests": [
+ {
+ "description": "no properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "one property is invalid",
+ "data": { "foo": 1 },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/maximum.json b/json/tests/draft7/maximum.json
new file mode 100644
index 0000000..6844a39
--- /dev/null
+++ b/json/tests/draft7/maximum.json
@@ -0,0 +1,54 @@
+[
+ {
+ "description": "maximum validation",
+ "schema": {"maximum": 3.0},
+ "tests": [
+ {
+ "description": "below the maximum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 3.0,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 3.5,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "maximum validation with unsigned integer",
+ "schema": {"maximum": 300},
+ "tests": [
+ {
+ "description": "below the maximum is invalid",
+ "data": 299.97,
+ "valid": true
+ },
+ {
+ "description": "boundary point integer is valid",
+ "data": 300,
+ "valid": true
+ },
+ {
+ "description": "boundary point float is valid",
+ "data": 300.00,
+ "valid": true
+ },
+ {
+ "description": "above the maximum is invalid",
+ "data": 300.5,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/minItems.json b/json/tests/draft7/minItems.json
new file mode 100644
index 0000000..ed51188
--- /dev/null
+++ b/json/tests/draft7/minItems.json
@@ -0,0 +1,28 @@
+[
+ {
+ "description": "minItems validation",
+ "schema": {"minItems": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": [1],
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "ignores non-arrays",
+ "data": "",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/minLength.json b/json/tests/draft7/minLength.json
new file mode 100644
index 0000000..3f09158
--- /dev/null
+++ b/json/tests/draft7/minLength.json
@@ -0,0 +1,33 @@
+[
+ {
+ "description": "minLength validation",
+ "schema": {"minLength": 2},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": "fo",
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": "f",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "one supplementary Unicode code point is not long enough",
+ "data": "\uD83D\uDCA9",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/minProperties.json b/json/tests/draft7/minProperties.json
new file mode 100644
index 0000000..49a0726
--- /dev/null
+++ b/json/tests/draft7/minProperties.json
@@ -0,0 +1,38 @@
+[
+ {
+ "description": "minProperties validation",
+ "schema": {"minProperties": 1},
+ "tests": [
+ {
+ "description": "longer is valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "exact length is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "too short is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/minimum.json b/json/tests/draft7/minimum.json
new file mode 100644
index 0000000..21ae50e
--- /dev/null
+++ b/json/tests/draft7/minimum.json
@@ -0,0 +1,69 @@
+[
+ {
+ "description": "minimum validation",
+ "schema": {"minimum": 1.1},
+ "tests": [
+ {
+ "description": "above the minimum is valid",
+ "data": 2.6,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "below the minimum is invalid",
+ "data": 0.6,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "minimum validation with signed integer",
+ "schema": {"minimum": -2},
+ "tests": [
+ {
+ "description": "negative above the minimum is valid",
+ "data": -1,
+ "valid": true
+ },
+ {
+ "description": "positive above the minimum is valid",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "boundary point is valid",
+ "data": -2,
+ "valid": true
+ },
+ {
+ "description": "boundary point with float is valid",
+ "data": -2.0,
+ "valid": true
+ },
+ {
+ "description": "float below the minimum is invalid",
+ "data": -2.0001,
+ "valid": false
+ },
+ {
+ "description": "int below the minimum is invalid",
+ "data": -3,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "x",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/multipleOf.json b/json/tests/draft7/multipleOf.json
new file mode 100644
index 0000000..faa87cf
--- /dev/null
+++ b/json/tests/draft7/multipleOf.json
@@ -0,0 +1,71 @@
+[
+ {
+ "description": "by int",
+ "schema": {"multipleOf": 2},
+ "tests": [
+ {
+ "description": "int by int",
+ "data": 10,
+ "valid": true
+ },
+ {
+ "description": "int by int fail",
+ "data": 7,
+ "valid": false
+ },
+ {
+ "description": "ignores non-numbers",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "by number",
+ "schema": {"multipleOf": 1.5},
+ "tests": [
+ {
+ "description": "zero is multiple of anything",
+ "data": 0,
+ "valid": true
+ },
+ {
+ "description": "4.5 is multiple of 1.5",
+ "data": 4.5,
+ "valid": true
+ },
+ {
+ "description": "35 is not multiple of 1.5",
+ "data": 35,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "by small number",
+ "schema": {"multipleOf": 0.0001},
+ "tests": [
+ {
+ "description": "0.0075 is multiple of 0.0001",
+ "data": 0.0075,
+ "valid": true
+ },
+ {
+ "description": "0.00751 is not multiple of 0.0001",
+ "data": 0.00751,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "invalid instance should not raise error when float division = inf",
+ "schema": {"type": "integer", "multipleOf": 0.123456789},
+ "tests": [
+ {
+ "description": "always invalid, but naive implementations may raise an overflow error",
+ "data": 1e308,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/not.json b/json/tests/draft7/not.json
new file mode 100644
index 0000000..98de0ed
--- /dev/null
+++ b/json/tests/draft7/not.json
@@ -0,0 +1,117 @@
+[
+ {
+ "description": "not",
+ "schema": {
+ "not": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "allowed",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "disallowed",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not multiple types",
+ "schema": {
+ "not": {"type": ["integer", "boolean"]}
+ },
+ "tests": [
+ {
+ "description": "valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "other mismatch",
+ "data": true,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not more complex schema",
+ "schema": {
+ "not": {
+ "type": "object",
+ "properties": {
+ "foo": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "other match",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"foo": "bar"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "forbidden property",
+ "schema": {
+ "properties": {
+ "foo": {
+ "not": {}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property present",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "property absent",
+ "data": {"bar": 1, "baz": 2},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema true",
+ "schema": {"not": true},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "not with boolean schema false",
+ "schema": {"not": false},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/oneOf.json b/json/tests/draft7/oneOf.json
new file mode 100644
index 0000000..eeb7ae8
--- /dev/null
+++ b/json/tests/draft7/oneOf.json
@@ -0,0 +1,274 @@
+[
+ {
+ "description": "oneOf",
+ "schema": {
+ "oneOf": [
+ {
+ "type": "integer"
+ },
+ {
+ "minimum": 2
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": 2.5,
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": 1.5,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with base schema",
+ "schema": {
+ "type": "string",
+ "oneOf" : [
+ {
+ "minLength": 2
+ },
+ {
+ "maxLength": 4
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "mismatch base schema",
+ "data": 3,
+ "valid": false
+ },
+ {
+ "description": "one oneOf valid",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all true",
+ "schema": {"oneOf": [true, true, true]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, one true",
+ "schema": {"oneOf": [true, false, false]},
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, more than one true",
+ "schema": {"oneOf": [true, true, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with boolean schemas, all false",
+ "schema": {"oneOf": [false, false, false]},
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf complex types",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": {"type": "integer"}
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": {"type": "string"}
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid (complex)",
+ "data": {"bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid (complex)",
+ "data": {"foo": "baz"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid (complex)",
+ "data": {"foo": "baz", "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid (complex)",
+ "data": {"foo": 2, "bar": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with empty schema",
+ "schema": {
+ "oneOf": [
+ { "type": "number" },
+ {}
+ ]
+ },
+ "tests": [
+ {
+ "description": "one valid - valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with required",
+ "schema": {
+ "type": "object",
+ "oneOf": [
+ { "required": ["foo", "bar"] },
+ { "required": ["foo", "baz"] }
+ ]
+ },
+ "tests": [
+ {
+ "description": "both invalid - invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "first valid - valid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": true
+ },
+ {
+ "description": "second valid - valid",
+ "data": {"foo": 1, "baz": 3},
+ "valid": true
+ },
+ {
+ "description": "both valid - invalid",
+ "data": {"foo": 1, "bar": 2, "baz" : 3},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "oneOf with missing optional property",
+ "schema": {
+ "oneOf": [
+ {
+ "properties": {
+ "bar": true,
+ "baz": true
+ },
+ "required": ["bar"]
+ },
+ {
+ "properties": {
+ "foo": true
+ },
+ "required": ["foo"]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "first oneOf valid",
+ "data": {"bar": 8},
+ "valid": true
+ },
+ {
+ "description": "second oneOf valid",
+ "data": {"foo": "foo"},
+ "valid": true
+ },
+ {
+ "description": "both oneOf valid",
+ "data": {"foo": "foo", "bar": 8},
+ "valid": false
+ },
+ {
+ "description": "neither oneOf valid",
+ "data": {"baz": "quux"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "nested oneOf, to check validation semantics",
+ "schema": {
+ "oneOf": [
+ {
+ "oneOf": [
+ {
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "anything non-null is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/bignum.json b/json/tests/draft7/optional/bignum.json
new file mode 100644
index 0000000..3f49226
--- /dev/null
+++ b/json/tests/draft7/optional/bignum.json
@@ -0,0 +1,93 @@
+[
+ {
+ "description": "integer",
+ "schema": { "type": "integer" },
+ "tests": [
+ {
+ "description": "a bignum is an integer",
+ "data": 12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is an integer",
+ "data": -12345678910111213141516171819202122232425262728293031,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "number",
+ "schema": { "type": "number" },
+ "tests": [
+ {
+ "description": "a bignum is a number",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ },
+ {
+ "description": "a negative bignum is a number",
+ "data": -98249283749234923498293171823948729348710298301928331,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "string",
+ "schema": { "type": "string" },
+ "tests": [
+ {
+ "description": "a bignum is not a string",
+ "data": 98249283749234923498293171823948729348710298301928331,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "maximum": 18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision",
+ "schema": {
+ "exclusiveMaximum": 972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for high numbers",
+ "data": 972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "integer comparison",
+ "schema": { "minimum": -18446744073709551615 },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -18446744073709551600,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "float comparison with high precision on negative numbers",
+ "schema": {
+ "exclusiveMinimum": -972783798187987123879878123.18878137
+ },
+ "tests": [
+ {
+ "description": "comparison works for very negative numbers",
+ "data": -972783798187987123879878123.188781371,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/content.json b/json/tests/draft7/optional/content.json
new file mode 100644
index 0000000..3f5a743
--- /dev/null
+++ b/json/tests/draft7/optional/content.json
@@ -0,0 +1,77 @@
+[
+ {
+ "description": "validation of string-encoded content based on media type",
+ "schema": {
+ "contentMediaType": "application/json"
+ },
+ "tests": [
+ {
+ "description": "a valid JSON document",
+ "data": "{\"foo\": \"bar\"}",
+ "valid": true
+ },
+ {
+ "description": "an invalid JSON document",
+ "data": "{:}",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary string-encoding",
+ "schema": {
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64 string",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "an invalid base64 string (% is not a valid character)",
+ "data": "eyJmb28iOi%iYmFyIn0K",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "validation of binary-encoded media type documents",
+ "schema": {
+ "contentMediaType": "application/json",
+ "contentEncoding": "base64"
+ },
+ "tests": [
+ {
+ "description": "a valid base64-encoded JSON document",
+ "data": "eyJmb28iOiAiYmFyIn0K",
+ "valid": true
+ },
+ {
+ "description": "a validly-encoded invalid JSON document",
+ "data": "ezp9Cg==",
+ "valid": false
+ },
+ {
+ "description": "an invalid base64 string that is valid JSON",
+ "data": "{}",
+ "valid": false
+ },
+ {
+ "description": "ignores non-strings",
+ "data": 100,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/ecmascript-regex.json b/json/tests/draft7/optional/ecmascript-regex.json
new file mode 100644
index 0000000..1beb0b3
--- /dev/null
+++ b/json/tests/draft7/optional/ecmascript-regex.json
@@ -0,0 +1,552 @@
+[
+ {
+ "description": "ECMA 262 regex $ does not match trailing newline",
+ "schema": {
+ "type": "string",
+ "pattern": "^abc$"
+ },
+ "tests": [
+ {
+ "description": "matches in Python, but should not in jsonschema",
+ "data": "abc\\n",
+ "valid": false
+ },
+ {
+ "description": "should match",
+ "data": "abc",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex converts \\t to horizontal tab",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\t$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\t",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0009",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and upper letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cC$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cC",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 regex escapes control codes with \\c and lower letter",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\cc$"
+ },
+ "tests": [
+ {
+ "description": "does not match",
+ "data": "\\cc",
+ "valid": false
+ },
+ {
+ "description": "matches",
+ "data": "\u0003",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\d matches ascii digits only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\d$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero matches",
+ "data": "0",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO does not match (unlike e.g. Python)",
+ "data": "߀",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) does not match",
+ "data": "\u07c0",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\D matches everything but ascii digits",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\D$"
+ },
+ "tests": [
+ {
+ "description": "ASCII zero does not match",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "NKO DIGIT ZERO matches (unlike e.g. Python)",
+ "data": "߀",
+ "valid": true
+ },
+ {
+ "description": "NKO DIGIT ZERO (as \\u escape) matches",
+ "data": "\u07c0",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\w matches ascii letters only",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\w$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' matches",
+ "data": "a",
+ "valid": true
+ },
+ {
+ "description": "latin-1 e-acute does not match (unlike e.g. Python)",
+ "data": "é",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\W matches everything but ascii letters",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\W$"
+ },
+ "tests": [
+ {
+ "description": "ASCII 'a' does not match",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "latin-1 e-acute matches (unlike e.g. Python)",
+ "data": "é",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\s matches whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\s$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space matches",
+ "data": " ",
+ "valid": true
+ },
+ {
+ "description": "Character tabulation matches",
+ "data": "\t",
+ "valid": true
+ },
+ {
+ "description": "Line tabulation matches",
+ "data": "\u000b",
+ "valid": true
+ },
+ {
+ "description": "Form feed matches",
+ "data": "\u000c",
+ "valid": true
+ },
+ {
+ "description": "latin-1 non-breaking-space matches",
+ "data": "\u00a0",
+ "valid": true
+ },
+ {
+ "description": "zero-width whitespace matches",
+ "data": "\ufeff",
+ "valid": true
+ },
+ {
+ "description": "line feed matches (line terminator)",
+ "data": "\u000a",
+ "valid": true
+ },
+ {
+ "description": "paragraph separator matches (line terminator)",
+ "data": "\u2029",
+ "valid": true
+ },
+ {
+ "description": "EM SPACE matches (Space_Separator)",
+ "data": "\u2003",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace control does not match",
+ "data": "\u0001",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace does not match",
+ "data": "\u2013",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ECMA 262 \\S matches everything but whitespace",
+ "schema": {
+ "type": "string",
+ "pattern": "^\\S$"
+ },
+ "tests": [
+ {
+ "description": "ASCII space does not match",
+ "data": " ",
+ "valid": false
+ },
+ {
+ "description": "Character tabulation does not match",
+ "data": "\t",
+ "valid": false
+ },
+ {
+ "description": "Line tabulation does not match",
+ "data": "\u000b",
+ "valid": false
+ },
+ {
+ "description": "Form feed does not match",
+ "data": "\u000c",
+ "valid": false
+ },
+ {
+ "description": "latin-1 non-breaking-space does not match",
+ "data": "\u00a0",
+ "valid": false
+ },
+ {
+ "description": "zero-width whitespace does not match",
+ "data": "\ufeff",
+ "valid": false
+ },
+ {
+ "description": "line feed does not match (line terminator)",
+ "data": "\u000a",
+ "valid": false
+ },
+ {
+ "description": "paragraph separator does not match (line terminator)",
+ "data": "\u2029",
+ "valid": false
+ },
+ {
+ "description": "EM SPACE does not match (Space_Separator)",
+ "data": "\u2003",
+ "valid": false
+ },
+ {
+ "description": "Non-whitespace control matches",
+ "data": "\u0001",
+ "valid": true
+ },
+ {
+ "description": "Non-whitespace matches",
+ "data": "\u2013",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all pattern matching",
+ "schema": { "pattern": "\\p{Letter}cole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patterns matches [A-Za-z0-9_], not unicode letters",
+ "schema": { "pattern": "\\wcole" },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": "LES HIVERS DE MON ENFANCE ÉTAIENT DES SAISONS LONGUES, LONGUES. NOUS VIVIONS EN TROIS LIEUX: L'ÉCOLE, L'ÉGLISE ET LA PATINOIRE; MAIS LA VRAIE VIE ÉTAIT SUR LA PATINOIRE.",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": { "pattern": "[a-z]cole" },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'école, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": "Les hivers de mon enfance étaient des saisons longues, longues. Nous vivions en trois lieux: l'\u00e9cole, l'église et la patinoire; mais la vraie vie était sur la patinoire.",
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": "Les hivers de mon enfance etaient des saisons longues, longues. Nous vivions en trois lieux: l'ecole, l'eglise et la patinoire; mais la vraie vie etait sur la patinoire.",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in pattern matches [0-9], not unicode digits",
+ "schema": { "pattern": "^\\d+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": { "pattern": "^\\p{digit}+$" },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": "42",
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": "-%#",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": "৪২",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "unicode semantics should be used for all patternProperties matching",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\p{Letter}cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "\\w in patternProperties matches [A-Za-z0-9_], not unicode letters",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "\\wcole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii character in json string",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ },
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode matching is case-sensitive",
+ "data": { "L'ÉCOLE": "PAS DE VRAIE VIE" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode characters do not match ascii ranges",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "[a-z]cole": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "literal unicode character in json string",
+ "data": { "l'école": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "unicode character in hex format in string",
+ "data": { "l'\u00e9cole": "pas de vraie vie" },
+ "valid": false
+ },
+ {
+ "description": "ascii characters match",
+ "data": { "l'ecole": "pas de vraie vie" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "\\d in patternProperties matches [0-9], not unicode digits",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\d+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "unicode digits are more than 0 through 9",
+ "schema": {
+ "type": "object",
+ "patternProperties": {
+ "^\\p{digit}+$": true
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "ascii digits",
+ "data": { "42": "life, the universe, and everything" },
+ "valid": true
+ },
+ {
+ "description": "ascii non-digits",
+ "data": { "-%#": "spending the year dead for tax reasons" },
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits (BENGALI DIGIT FOUR, BENGALI DIGIT TWO)",
+ "data": { "৪২": "khajit has wares if you have coin" },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/float-overflow.json b/json/tests/draft7/optional/float-overflow.json
new file mode 100644
index 0000000..52ff982
--- /dev/null
+++ b/json/tests/draft7/optional/float-overflow.json
@@ -0,0 +1,13 @@
+[
+ {
+ "description": "all integers are multiples of 0.5, if overflow is handled",
+ "schema": {"type": "integer", "multipleOf": 0.5},
+ "tests": [
+ {
+ "description": "valid if optional overflow handling is implemented",
+ "data": 1e308,
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/date-time.json b/json/tests/draft7/optional/format/date-time.json
new file mode 100644
index 0000000..f4f9933
--- /dev/null
+++ b/json/tests/draft7/optional/format/date-time.json
@@ -0,0 +1,133 @@
+[
+ {
+ "description": "validation of date-time strings",
+ "schema": { "format": "date-time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string",
+ "data": "1963-06-19T08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string without second fraction",
+ "data": "1963-06-19T08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with plus offset",
+ "data": "1937-01-01T12:00:27.87+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time string with minus offset",
+ "data": "1990-12-31T15:59:50.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, UTC",
+ "data": "1998-12-31T23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "a valid date-time with a leap second, with minus offset",
+ "data": "1998-12-31T15:59:60.123-08:00",
+ "valid": true
+ },
+ {
+ "description": "an invalid date-time past leap second, UTC",
+ "data": "1998-12-31T23:59:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong minute, UTC",
+ "data": "1998-12-31T23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time with leap second on a wrong hour, UTC",
+ "data": "1998-12-31T22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid day in date-time string",
+ "data": "1990-02-31T15:59:59.123-08:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset in date-time string",
+ "data": "1990-12-31T15:59:59-24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid closing Z after time-zone offset",
+ "data": "1963-06-19T08:30:06.28123+01:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid date-time string",
+ "data": "06/19/1963 08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "case-insensitive T and Z",
+ "data": "1963-06-19t08:30:06.283185z",
+ "valid": true
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350T01:01:01",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded month dates",
+ "data": "1963-6-19T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "invalid non-padded day dates",
+ "data": "1963-06-1T08:30:06.283185Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the date portion",
+ "data": "1963-06-1৪T00:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the time portion",
+ "data": "1963-06-11T0৪:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/date.json b/json/tests/draft7/optional/format/date.json
new file mode 100644
index 0000000..b4f61ee
--- /dev/null
+++ b/json/tests/draft7/optional/format/date.json
@@ -0,0 +1,223 @@
+[
+ {
+ "description": "validation of date strings",
+ "schema": { "format": "date" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid date string",
+ "data": "1963-06-19",
+ "valid": true
+ },
+ {
+ "description": "a valid date string with 31 days in January",
+ "data": "2020-01-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in January",
+ "data": "2020-01-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 28 days in February (normal)",
+ "data": "2021-02-28",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 29 days in February (normal)",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 29 days in February (leap)",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 30 days in February (leap)",
+ "data": "2020-02-30",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in March",
+ "data": "2020-03-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in March",
+ "data": "2020-03-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in April",
+ "data": "2020-04-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in April",
+ "data": "2020-04-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in May",
+ "data": "2020-05-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in May",
+ "data": "2020-05-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in June",
+ "data": "2020-06-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in June",
+ "data": "2020-06-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in July",
+ "data": "2020-07-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in July",
+ "data": "2020-07-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in August",
+ "data": "2020-08-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in August",
+ "data": "2020-08-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in September",
+ "data": "2020-09-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in September",
+ "data": "2020-09-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in October",
+ "data": "2020-10-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in October",
+ "data": "2020-10-32",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 30 days in November",
+ "data": "2020-11-30",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 31 days in November",
+ "data": "2020-11-31",
+ "valid": false
+ },
+ {
+ "description": "a valid date string with 31 days in December",
+ "data": "2020-12-31",
+ "valid": true
+ },
+ {
+ "description": "a invalid date string with 32 days in December",
+ "data": "2020-12-32",
+ "valid": false
+ },
+ {
+ "description": "a invalid date string with invalid month",
+ "data": "2020-13-01",
+ "valid": false
+ },
+ {
+ "description": "an invalid date string",
+ "data": "06/19/1963",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "2013-350",
+ "valid": false
+ },
+ {
+ "description": "non-padded month dates are not valid",
+ "data": "1998-1-20",
+ "valid": false
+ },
+ {
+ "description": "non-padded day dates are not valid",
+ "data": "1998-01-1",
+ "valid": false
+ },
+ {
+ "description": "invalid month",
+ "data": "1998-13-01",
+ "valid": false
+ },
+ {
+ "description": "invalid month-day combination",
+ "data": "1998-04-31",
+ "valid": false
+ },
+ {
+ "description": "2021 is not a leap year",
+ "data": "2021-02-29",
+ "valid": false
+ },
+ {
+ "description": "2020 is a leap year",
+ "data": "2020-02-29",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1963-06-1৪",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/email.json b/json/tests/draft7/optional/format/email.json
new file mode 100644
index 0000000..d6761a4
--- /dev/null
+++ b/json/tests/draft7/optional/format/email.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of e-mail addresses",
+ "schema": { "format": "email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "tilde in local part is valid",
+ "data": "te~st@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde before local part is valid",
+ "data": "~test@example.com",
+ "valid": true
+ },
+ {
+ "description": "tilde after local part is valid",
+ "data": "test~@example.com",
+ "valid": true
+ },
+ {
+ "description": "dot before local part is not valid",
+ "data": ".test@example.com",
+ "valid": false
+ },
+ {
+ "description": "dot after local part is not valid",
+ "data": "test.@example.com",
+ "valid": false
+ },
+ {
+ "description": "two separated dots inside local part are valid",
+ "data": "te.s.t@example.com",
+ "valid": true
+ },
+ {
+ "description": "two subsequent dots inside local part are not valid",
+ "data": "te..st@example.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/hostname.json b/json/tests/draft7/optional/format/hostname.json
new file mode 100644
index 0000000..8a67fda
--- /dev/null
+++ b/json/tests/draft7/optional/format/hostname.json
@@ -0,0 +1,98 @@
+[
+ {
+ "description": "validation of host names",
+ "schema": { "format": "hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name",
+ "data": "www.example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid punycoded IDN hostname",
+ "data": "xn--4gbwdl.xn--wgbh1c",
+ "valid": true
+ },
+ {
+ "description": "a host name starting with an illegal character",
+ "data": "-a-host-name-that-starts-with--",
+ "valid": false
+ },
+ {
+ "description": "a host name containing illegal characters",
+ "data": "not_a_valid_host_name",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",
+ "valid": false
+ },
+ {
+ "description": "starts with hyphen",
+ "data": "-hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with hyphen",
+ "data": "hostname-",
+ "valid": false
+ },
+ {
+ "description": "starts with underscore",
+ "data": "_hostname",
+ "valid": false
+ },
+ {
+ "description": "ends with underscore",
+ "data": "hostname_",
+ "valid": false
+ },
+ {
+ "description": "contains underscore",
+ "data": "host_name",
+ "valid": false
+ },
+ {
+ "description": "maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com",
+ "valid": true
+ },
+ {
+ "description": "exceeds maximum label length",
+ "data": "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/idn-email.json b/json/tests/draft7/optional/format/idn-email.json
new file mode 100644
index 0000000..6e21374
--- /dev/null
+++ b/json/tests/draft7/optional/format/idn-email.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "validation of an internationalized e-mail addresses",
+ "schema": { "format": "idn-email" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid idn e-mail (example@example.test in Hangul)",
+ "data": "실례@실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "an invalid idn e-mail address",
+ "data": "2962",
+ "valid": false
+ },
+ {
+ "description": "a valid e-mail address",
+ "data": "joe.bloggs@example.com",
+ "valid": true
+ },
+ {
+ "description": "an invalid e-mail address",
+ "data": "2962",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/idn-hostname.json b/json/tests/draft7/optional/format/idn-hostname.json
new file mode 100644
index 0000000..6c8f86a
--- /dev/null
+++ b/json/tests/draft7/optional/format/idn-hostname.json
@@ -0,0 +1,304 @@
+[
+ {
+ "description": "validation of internationalized host names",
+ "schema": { "format": "idn-hostname" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid host name (example.test in Hangul)",
+ "data": "실례.테스트",
+ "valid": true
+ },
+ {
+ "description": "illegal first char U+302E Hangul single dot tone mark",
+ "data": "〮실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "contains illegal char U+302E Hangul single dot tone mark",
+ "data": "실〮례.테스트",
+ "valid": false
+ },
+ {
+ "description": "a host name with a component too long",
+ "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트",
+ "valid": false
+ },
+ {
+ "description": "invalid label, correct Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc3492#section-7.1",
+ "data": "-> $1.00 <--",
+ "valid": false
+ },
+ {
+ "description": "valid Chinese Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5890#section-2.3.2.1 https://tools.ietf.org/html/rfc5891#section-4.4",
+ "data": "xn--ihqwcrb4cv8a8dqg056pqjye",
+ "valid": true
+ },
+ {
+ "description": "invalid Punycode",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.4 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "xn--X",
+ "valid": false
+ },
+ {
+ "description": "U-label contains \"--\" in the 3rd and 4th position",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1 https://tools.ietf.org/html/rfc5890#section-2.3.2.1",
+ "data": "XN--aa---o47jg78q",
+ "valid": false
+ },
+ {
+ "description": "U-label starts with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello",
+ "valid": false
+ },
+ {
+ "description": "U-label ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "hello-",
+ "valid": false
+ },
+ {
+ "description": "U-label starts and ends with a dash",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.1",
+ "data": "-hello-",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Spacing Combining Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0903hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with a Nonspacing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0300hello",
+ "valid": false
+ },
+ {
+ "description": "Begins with an Enclosing Mark",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.2",
+ "data": "\u0488hello",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are PVALID, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u00df\u03c2\u0f0b\u3007",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are PVALID, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u06fd\u06fe",
+ "valid": true
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, right-to-left chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6",
+ "data": "\u0640\u07fa",
+ "valid": false
+ },
+ {
+ "description": "Exceptions that are DISALLOWED, left-to-right chars",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.2 https://tools.ietf.org/html/rfc5892#section-2.6 Note: The two combining marks (U+302E and U+302F) are in the middle and not at the start",
+ "data": "\u3031\u3032\u3033\u3034\u3035\u302e\u302f\u303b",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no preceding 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "a\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing preceding",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "\u00b7l",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with no following 'l'",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7a",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with nothing following",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7",
+ "valid": false
+ },
+ {
+ "description": "MIDDLE DOT with surrounding 'l's",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.3",
+ "data": "l\u00b7l",
+ "valid": true
+ },
+ {
+ "description": "Greek KERAIA not followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375S",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA not followed by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375",
+ "valid": false
+ },
+ {
+ "description": "Greek KERAIA followed by Greek",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.4",
+ "data": "\u03b1\u0375\u03b2",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERESH not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "A\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05f3\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERESH preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.5",
+ "data": "\u05d0\u05f3\u05d1",
+ "valid": true
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "A\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05f4\u05d1",
+ "valid": false
+ },
+ {
+ "description": "Hebrew GERSHAYIM preceded by Hebrew",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.6",
+ "data": "\u05d0\u05f4\u05d1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no Hiragana, Katakana, or Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "def\u30fbabc",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with no other characters",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb",
+ "valid": false
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Hiragana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u3041",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Katakana",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u30a1",
+ "valid": true
+ },
+ {
+ "description": "KATAKANA MIDDLE DOT with Han",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.7",
+ "data": "\u30fb\u4e08",
+ "valid": true
+ },
+ {
+ "description": "Arabic-Indic digits mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0660\u06f0",
+ "valid": false
+ },
+ {
+ "description": "Arabic-Indic digits not mixed with Extended Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.8",
+ "data": "\u0628\u0660\u0628",
+ "valid": true
+ },
+ {
+ "description": "Extended Arabic-Indic digits not mixed with Arabic-Indic digits",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.9",
+ "data": "\u06f00",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER not preceded by anything",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u200d\u0937",
+ "valid": false
+ },
+ {
+ "description": "ZERO WIDTH JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.2 https://www.unicode.org/review/pr-37.pdf",
+ "data": "\u0915\u094d\u200d\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER preceded by Virama",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1",
+ "data": "\u0915\u094d\u200c\u0937",
+ "valid": true
+ },
+ {
+ "description": "ZERO WIDTH NON-JOINER not preceded by Virama but matches regexp",
+ "comment": "https://tools.ietf.org/html/rfc5891#section-4.2.3.3 https://tools.ietf.org/html/rfc5892#appendix-A.1 https://www.w3.org/TR/alreq/#h_disjoining_enforcement",
+ "data": "\u0628\u064a\u200c\u0628\u064a",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/ipv4.json b/json/tests/draft7/optional/format/ipv4.json
new file mode 100644
index 0000000..6b166c7
--- /dev/null
+++ b/json/tests/draft7/optional/format/ipv4.json
@@ -0,0 +1,84 @@
+[
+ {
+ "description": "validation of IP addresses",
+ "schema": { "format": "ipv4" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IP address",
+ "data": "192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "an IP address with too many components",
+ "data": "127.0.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "an IP address with out-of-range values",
+ "data": "256.256.256.256",
+ "valid": false
+ },
+ {
+ "description": "an IP address without 4 components",
+ "data": "127.0",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer",
+ "data": "0x7f000001",
+ "valid": false
+ },
+ {
+ "description": "an IP address as an integer (decimal)",
+ "data": "2130706433",
+ "valid": false
+ },
+ {
+ "description": "leading zeroes should be rejected, as they are treated as octals",
+ "comment": "see https://sick.codes/universal-netmask-npm-package-used-by-270000-projects-vulnerable-to-octal-input-data-server-side-request-forgery-remote-file-inclusion-local-file-inclusion-and-more-cve-2021-28918/",
+ "data": "087.10.0.1",
+ "valid": false
+ },
+ {
+ "description": "value without leading zero is valid",
+ "data": "87.10.0.1",
+ "valid": true
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২7.0.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/ipv6.json b/json/tests/draft7/optional/format/ipv6.json
new file mode 100644
index 0000000..6379927
--- /dev/null
+++ b/json/tests/draft7/optional/format/ipv6.json
@@ -0,0 +1,208 @@
+[
+ {
+ "description": "validation of IPv6 addresses",
+ "schema": { "format": "ipv6" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IPv6 address",
+ "data": "::1",
+ "valid": true
+ },
+ {
+ "description": "an IPv6 address with out-of-range values",
+ "data": "12345::",
+ "valid": false
+ },
+ {
+ "description": "trailing 4 hex symbols is valid",
+ "data": "::abef",
+ "valid": true
+ },
+ {
+ "description": "trailing 5 hex symbols is invalid",
+ "data": "::abcef",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address with too many components",
+ "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",
+ "valid": false
+ },
+ {
+ "description": "an IPv6 address containing illegal characters",
+ "data": "::laptop",
+ "valid": false
+ },
+ {
+ "description": "no digits is valid",
+ "data": "::",
+ "valid": true
+ },
+ {
+ "description": "leading colons is valid",
+ "data": "::42:ff:1",
+ "valid": true
+ },
+ {
+ "description": "trailing colons is valid",
+ "data": "d6::",
+ "valid": true
+ },
+ {
+ "description": "missing leading octet is invalid",
+ "data": ":2:3:4:5:6:7:8",
+ "valid": false
+ },
+ {
+ "description": "missing trailing octet is invalid",
+ "data": "1:2:3:4:5:6:7:",
+ "valid": false
+ },
+ {
+ "description": "missing leading octet with omitted octets later",
+ "data": ":2:3:4::8",
+ "valid": false
+ },
+ {
+ "description": "single set of double colons in the middle is valid",
+ "data": "1:d6::42",
+ "valid": true
+ },
+ {
+ "description": "two sets of double colons is invalid",
+ "data": "1::d6::42",
+ "valid": false
+ },
+ {
+ "description": "mixed format with the ipv4 section as decimal octets",
+ "data": "1::d6:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with double colons between the sections",
+ "data": "1:2::192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "mixed format with ipv4 section with octet out of range",
+ "data": "1::2:192.168.256.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with ipv4 section with a hex octet",
+ "data": "1::2:192.168.ff.1",
+ "valid": false
+ },
+ {
+ "description": "mixed format with leading double colons (ipv4-mapped ipv6 address)",
+ "data": "::ffff:192.168.0.1",
+ "valid": true
+ },
+ {
+ "description": "triple colons is invalid",
+ "data": "1:2:3:4:5:::8",
+ "valid": false
+ },
+ {
+ "description": "8 octets",
+ "data": "1:2:3:4:5:6:7:8",
+ "valid": true
+ },
+ {
+ "description": "insufficient octets without double colons",
+ "data": "1:2:3:4:5:6:7",
+ "valid": false
+ },
+ {
+ "description": "no colons is invalid",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 is not ipv6",
+ "data": "127.0.0.1",
+ "valid": false
+ },
+ {
+ "description": "ipv4 segment must have 4 octets",
+ "data": "1:2:3:4:1.2.3",
+ "valid": false
+ },
+ {
+ "description": "leading whitespace is invalid",
+ "data": " ::1",
+ "valid": false
+ },
+ {
+ "description": "trailing whitespace is invalid",
+ "data": "::1 ",
+ "valid": false
+ },
+ {
+ "description": "netmask is not a part of ipv6 address",
+ "data": "fe80::/64",
+ "valid": false
+ },
+ {
+ "description": "zone id is not a part of ipv6 address",
+ "data": "fe80::a%eth1",
+ "valid": false
+ },
+ {
+ "description": "a long valid ipv6",
+ "data": "1000:1000:1000:1000:1000:1000:255.255.255.255",
+ "valid": true
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, first",
+ "data": "100:100:100:100:100:100:255.255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "a long invalid ipv6, below length limit, second",
+ "data": "100:100:100:100:100:100:100:255.255.255.255",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1:2:3:4:5:6:7:৪",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected in the ipv4 portion also",
+ "data": "1:2::192.16৪.0.1",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/iri-reference.json b/json/tests/draft7/optional/format/iri-reference.json
new file mode 100644
index 0000000..c6b4c22
--- /dev/null
+++ b/json/tests/draft7/optional/format/iri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of IRI References",
+ "schema": { "format": "iri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative IRI Reference",
+ "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid relative IRI Reference",
+ "data": "/âππ",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI Reference",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "a valid IRI Reference",
+ "data": "âππ",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI fragment",
+ "data": "#ƒrägmênt",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI fragment",
+ "data": "#ƒräg\\mênt",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/iri.json b/json/tests/draft7/optional/format/iri.json
new file mode 100644
index 0000000..a0d12ae
--- /dev/null
+++ b/json/tests/draft7/optional/format/iri.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of IRIs",
+ "schema": { "format": "iri" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag",
+ "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with anchor tag and parentheses",
+ "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with URL-encoded stuff",
+ "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid IRI based on IPv6",
+ "data": "http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
+ "valid": true
+ },
+ {
+ "description": "an invalid IRI based on IPv6",
+ "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative IRI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI",
+ "data": "\\\\WINDOWS\\filëßåré",
+ "valid": false
+ },
+ {
+ "description": "an invalid IRI though valid IRI reference",
+ "data": "âππ",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/json-pointer.json b/json/tests/draft7/optional/format/json-pointer.json
new file mode 100644
index 0000000..a0346b5
--- /dev/null
+++ b/json/tests/draft7/optional/format/json-pointer.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of JSON-pointers (JSON String Representation)",
+ "schema": { "format": "json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid JSON-pointer",
+ "data": "/foo/bar~0/baz~1/%a",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (~ not escaped)",
+ "data": "/foo/bar~",
+ "valid": false
+ },
+ {
+ "description": "valid JSON-pointer with empty segment",
+ "data": "/foo//bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer with the last empty segment",
+ "data": "/foo/bar/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #1",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #2",
+ "data": "/foo",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #3",
+ "data": "/foo/0",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #4",
+ "data": "/",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #5",
+ "data": "/a~1b",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #6",
+ "data": "/c%d",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #7",
+ "data": "/e^f",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #8",
+ "data": "/g|h",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #9",
+ "data": "/i\\j",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #10",
+ "data": "/k\"l",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #11",
+ "data": "/ ",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer as stated in RFC 6901 #12",
+ "data": "/m~0n",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer used adding to the last array position",
+ "data": "/foo/-",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (- used as object member name)",
+ "data": "/foo/-/bar",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (multiple escaped characters)",
+ "data": "/~1~0~0~1~1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #1",
+ "data": "/~1.1",
+ "valid": true
+ },
+ {
+ "description": "valid JSON-pointer (escaped with fraction part) #2",
+ "data": "/~0.1",
+ "valid": true
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #1",
+ "data": "#",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #2",
+ "data": "#/",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (URI Fragment Identifier) #3",
+ "data": "#a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #1",
+ "data": "/~0~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (some escaped, but not all) #2",
+ "data": "/~0/~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #1",
+ "data": "/~2",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (wrong escape character) #2",
+ "data": "/~-1",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (multiple characters not escaped)",
+ "data": "/~~",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2",
+ "data": "0",
+ "valid": false
+ },
+ {
+ "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3",
+ "data": "a/a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/regex.json b/json/tests/draft7/optional/format/regex.json
new file mode 100644
index 0000000..3449177
--- /dev/null
+++ b/json/tests/draft7/optional/format/regex.json
@@ -0,0 +1,48 @@
+[
+ {
+ "description": "validation of regular expressions",
+ "schema": { "format": "regex" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid regular expression",
+ "data": "([abc])+\\s+$",
+ "valid": true
+ },
+ {
+ "description": "a regular expression with unclosed parens is invalid",
+ "data": "^(abc]",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/relative-json-pointer.json b/json/tests/draft7/optional/format/relative-json-pointer.json
new file mode 100644
index 0000000..9309986
--- /dev/null
+++ b/json/tests/draft7/optional/format/relative-json-pointer.json
@@ -0,0 +1,83 @@
+[
+ {
+ "description": "validation of Relative JSON Pointers (RJP)",
+ "schema": { "format": "relative-json-pointer" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid upwards RJP",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "a valid downwards RJP",
+ "data": "0/foo/bar",
+ "valid": true
+ },
+ {
+ "description": "a valid up and then down RJP, with array index",
+ "data": "2/0/baz/1/zip",
+ "valid": true
+ },
+ {
+ "description": "a valid RJP taking the member or index name",
+ "data": "0#",
+ "valid": true
+ },
+ {
+ "description": "an invalid RJP that is a valid JSON Pointer",
+ "data": "/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "negative prefix",
+ "data": "-1/foo/bar",
+ "valid": false
+ },
+ {
+ "description": "## is not a valid json-pointer",
+ "data": "0##",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus json-pointer",
+ "data": "01/a",
+ "valid": false
+ },
+ {
+ "description": "zero cannot be followed by other digits, plus octothorpe",
+ "data": "01#",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/time.json b/json/tests/draft7/optional/format/time.json
new file mode 100644
index 0000000..5011d78
--- /dev/null
+++ b/json/tests/draft7/optional/format/time.json
@@ -0,0 +1,198 @@
+[
+ {
+ "description": "validation of time strings",
+ "schema": { "format": "time" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid time string",
+ "data": "08:30:06Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with leap second, Zulu",
+ "data": "23:59:60Z",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, Zulu (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, zero time-offset",
+ "data": "23:59:60+00:00",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong hour)",
+ "data": "22:59:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, zero time-offset (wrong minute)",
+ "data": "23:58:60+00:00",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, positive time-offset",
+ "data": "01:29:60+01:30",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large positive time-offset",
+ "data": "23:29:60+23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong hour)",
+ "data": "23:59:60+01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, positive time-offset (wrong minute)",
+ "data": "23:59:60+00:30",
+ "valid": false
+ },
+ {
+ "description": "valid leap second, negative time-offset",
+ "data": "15:59:60-08:00",
+ "valid": true
+ },
+ {
+ "description": "valid leap second, large negative time-offset",
+ "data": "00:29:60-23:30",
+ "valid": true
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong hour)",
+ "data": "23:59:60-01:00",
+ "valid": false
+ },
+ {
+ "description": "invalid leap second, negative time-offset (wrong minute)",
+ "data": "23:59:60-00:30",
+ "valid": false
+ },
+ {
+ "description": "a valid time string with second fraction",
+ "data": "23:20:50.52Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with precise second fraction",
+ "data": "08:30:06.283185Z",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with plus offset",
+ "data": "08:30:06+00:20",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with minus offset",
+ "data": "08:30:06-08:00",
+ "valid": true
+ },
+ {
+ "description": "a valid time string with case-insensitive Z",
+ "data": "08:30:06z",
+ "valid": true
+ },
+ {
+ "description": "an invalid time string with invalid hour",
+ "data": "24:00:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid minute",
+ "data": "00:60:00Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid second",
+ "data": "00:00:61Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong hour)",
+ "data": "22:59:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid leap second (wrong minute)",
+ "data": "23:58:60Z",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset hour",
+ "data": "01:02:03+24:00",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time numoffset minute",
+ "data": "01:02:03+00:60",
+ "valid": false
+ },
+ {
+ "description": "an invalid time string with invalid time with both Z and numoffset",
+ "data": "01:02:03Z+00:30",
+ "valid": false
+ },
+ {
+ "description": "an invalid offset indicator",
+ "data": "08:30:06 PST",
+ "valid": false
+ },
+ {
+ "description": "only RFC3339 not all of ISO 8601 are valid",
+ "data": "01:01:01,1111",
+ "valid": false
+ },
+ {
+ "description": "no time offset",
+ "data": "12:00:00",
+ "valid": false
+ },
+ {
+ "description": "non-ascii digits should be rejected",
+ "data": "1২:00:00Z",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/unknown.json b/json/tests/draft7/optional/format/unknown.json
new file mode 100644
index 0000000..12339ae
--- /dev/null
+++ b/json/tests/draft7/optional/format/unknown.json
@@ -0,0 +1,43 @@
+[
+ {
+ "description": "unknown format",
+ "schema": { "format": "unknown" },
+ "tests": [
+ {
+ "description": "unknown formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "unknown formats ignore strings",
+ "data": "string",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/uri-reference.json b/json/tests/draft7/optional/format/uri-reference.json
new file mode 100644
index 0000000..7cdf228
--- /dev/null
+++ b/json/tests/draft7/optional/format/uri-reference.json
@@ -0,0 +1,73 @@
+[
+ {
+ "description": "validation of URI References",
+ "schema": { "format": "uri-reference" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid URI",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid relative URI Reference",
+ "data": "/abc",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI Reference",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "a valid URI Reference",
+ "data": "abc",
+ "valid": true
+ },
+ {
+ "description": "a valid URI fragment",
+ "data": "#fragment",
+ "valid": true
+ },
+ {
+ "description": "an invalid URI fragment",
+ "data": "#frag\\ment",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/uri-template.json b/json/tests/draft7/optional/format/uri-template.json
new file mode 100644
index 0000000..df355c5
--- /dev/null
+++ b/json/tests/draft7/optional/format/uri-template.json
@@ -0,0 +1,58 @@
+[
+ {
+ "description": "format: uri-template",
+ "schema": { "format": "uri-template" },
+ "tests": [
+ {
+ "description": "all string formats ignore integers",
+ "data": 12,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore floats",
+ "data": 13.7,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore booleans",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "all string formats ignore nulls",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "a valid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term}",
+ "valid": true
+ },
+ {
+ "description": "an invalid uri-template",
+ "data": "http://example.com/dictionary/{term:1}/{term",
+ "valid": false
+ },
+ {
+ "description": "a valid uri-template without variables",
+ "data": "http://example.com/dictionary",
+ "valid": true
+ },
+ {
+ "description": "a valid relative uri-template",
+ "data": "dictionary/{term:1}/{term}",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/format/uri.json b/json/tests/draft7/optional/format/uri.json
new file mode 100644
index 0000000..792d71a
--- /dev/null
+++ b/json/tests/draft7/optional/format/uri.json
@@ -0,0 +1,108 @@
+[
+ {
+ "description": "validation of URIs",
+ "schema": { "format": "uri" },
+ "tests": [
+ {
+ "description": "a valid URL with anchor tag",
+ "data": "http://foo.bar/?baz=qux#quux",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with anchor tag and parentheses",
+ "data": "http://foo.com/blah_(wikipedia)_blah#cite-1",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with URL-encoded stuff",
+ "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff",
+ "valid": true
+ },
+ {
+ "description": "a valid puny-coded URL ",
+ "data": "http://xn--nw2a.xn--j6w193g/",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with many special characters",
+ "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid URL based on IPv4",
+ "data": "http://223.255.255.254",
+ "valid": true
+ },
+ {
+ "description": "a valid URL with ftp scheme",
+ "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL for a simple text file",
+ "data": "http://www.ietf.org/rfc/rfc2396.txt",
+ "valid": true
+ },
+ {
+ "description": "a valid URL ",
+ "data": "ldap://[2001:db8::7]/c=GB?objectClass?one",
+ "valid": true
+ },
+ {
+ "description": "a valid mailto URI",
+ "data": "mailto:John.Doe@example.com",
+ "valid": true
+ },
+ {
+ "description": "a valid newsgroup URI",
+ "data": "news:comp.infosystems.www.servers.unix",
+ "valid": true
+ },
+ {
+ "description": "a valid tel URI",
+ "data": "tel:+1-816-555-1212",
+ "valid": true
+ },
+ {
+ "description": "a valid URN",
+ "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2",
+ "valid": true
+ },
+ {
+ "description": "an invalid protocol-relative URI Reference",
+ "data": "//foo.bar/?baz=qux#quux",
+ "valid": false
+ },
+ {
+ "description": "an invalid relative URI Reference",
+ "data": "/abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI",
+ "data": "\\\\WINDOWS\\fileshare",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI though valid URI reference",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces",
+ "data": "http:// shouldfail.com",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with spaces and missing scheme",
+ "data": ":// should fail",
+ "valid": false
+ },
+ {
+ "description": "an invalid URI with comma in scheme",
+ "data": "bar,baz:foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/optional/non-bmp-regex.json b/json/tests/draft7/optional/non-bmp-regex.json
new file mode 100644
index 0000000..dd67af2
--- /dev/null
+++ b/json/tests/draft7/optional/non-bmp-regex.json
@@ -0,0 +1,82 @@
+[
+ {
+ "description": "Proper UTF-16 surrogate pair handling: pattern",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": { "pattern": "^🐲*$" },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": "🐲",
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": "🐲🐲",
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": "🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": "🐉🐉",
+ "valid": false
+ },
+ {
+ "description": "doesn't match one ASCII",
+ "data": "D",
+ "valid": false
+ },
+ {
+ "description": "doesn't match two ASCII",
+ "data": "DD",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Proper UTF-16 surrogate pair handling: patternProperties",
+ "comment": "Optional because .Net doesn't correctly handle 32-bit Unicode characters",
+ "schema": {
+ "patternProperties": {
+ "^🐲*$": {
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "matches empty",
+ "data": { "": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches single",
+ "data": { "🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "matches two",
+ "data": { "🐲🐲": 1 },
+ "valid": true
+ },
+ {
+ "description": "doesn't match one",
+ "data": { "🐲": "hello" },
+ "valid": false
+ },
+ {
+ "description": "doesn't match two",
+ "data": { "🐲🐲": "hello" },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/pattern.json b/json/tests/draft7/pattern.json
new file mode 100644
index 0000000..92db0f9
--- /dev/null
+++ b/json/tests/draft7/pattern.json
@@ -0,0 +1,59 @@
+[
+ {
+ "description": "pattern validation",
+ "schema": {"pattern": "^a*$"},
+ "tests": [
+ {
+ "description": "a matching pattern is valid",
+ "data": "aaa",
+ "valid": true
+ },
+ {
+ "description": "a non-matching pattern is invalid",
+ "data": "abc",
+ "valid": false
+ },
+ {
+ "description": "ignores booleans",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "ignores integers",
+ "data": 123,
+ "valid": true
+ },
+ {
+ "description": "ignores floats",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "ignores objects",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "pattern is not anchored",
+ "schema": {"pattern": "a+"},
+ "tests": [
+ {
+ "description": "matches a substring",
+ "data": "xxaayy",
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/patternProperties.json b/json/tests/draft7/patternProperties.json
new file mode 100644
index 0000000..c10ffcc
--- /dev/null
+++ b/json/tests/draft7/patternProperties.json
@@ -0,0 +1,156 @@
+[
+ {
+ "description":
+ "patternProperties validates properties matching a regex",
+ "schema": {
+ "patternProperties": {
+ "f.*o": {"type": "integer"}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "multiple valid matches is valid",
+ "data": {"foo": 1, "foooooo" : 2},
+ "valid": true
+ },
+ {
+ "description": "a single invalid match is invalid",
+ "data": {"foo": "bar", "fooooo": 2},
+ "valid": false
+ },
+ {
+ "description": "multiple invalid matches is invalid",
+ "data": {"foo": "bar", "foooooo" : "baz"},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": ["foo"],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple simultaneous patternProperties are validated",
+ "schema": {
+ "patternProperties": {
+ "a*": {"type": "integer"},
+ "aaa*": {"maximum": 20}
+ }
+ },
+ "tests": [
+ {
+ "description": "a single valid match is valid",
+ "data": {"a": 21},
+ "valid": true
+ },
+ {
+ "description": "a simultaneous match is valid",
+ "data": {"aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "multiple matches is valid",
+ "data": {"a": 21, "aaaa": 18},
+ "valid": true
+ },
+ {
+ "description": "an invalid due to one is invalid",
+ "data": {"a": "bar"},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to the other is invalid",
+ "data": {"aaaa": 31},
+ "valid": false
+ },
+ {
+ "description": "an invalid due to both is invalid",
+ "data": {"aaa": "foo", "aaaa": 31},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "regexes are not anchored by default and are case sensitive",
+ "schema": {
+ "patternProperties": {
+ "[0-9]{2,}": { "type": "boolean" },
+ "X_": { "type": "string" }
+ }
+ },
+ "tests": [
+ {
+ "description": "non recognized members are ignored",
+ "data": { "answer 1": "42" },
+ "valid": true
+ },
+ {
+ "description": "recognized members are accounted for",
+ "data": { "a31b": null },
+ "valid": false
+ },
+ {
+ "description": "regexes are case sensitive",
+ "data": { "a_x_3": 3 },
+ "valid": true
+ },
+ {
+ "description": "regexes are case sensitive, 2",
+ "data": { "a_X_3": 3 },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "patternProperties with boolean schemas",
+ "schema": {
+ "patternProperties": {
+ "f.*": true,
+ "b.*": false
+ }
+ },
+ "tests": [
+ {
+ "description": "object with property matching schema true is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "object with property matching schema false is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with both properties is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ },
+ {
+ "description": "object with a property matching both true and false is invalid",
+ "data": {"foobar":1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/properties.json b/json/tests/draft7/properties.json
new file mode 100644
index 0000000..b86c181
--- /dev/null
+++ b/json/tests/draft7/properties.json
@@ -0,0 +1,167 @@
+[
+ {
+ "description": "object properties validation",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "both properties present and valid is valid",
+ "data": {"foo": 1, "bar": "baz"},
+ "valid": true
+ },
+ {
+ "description": "one property invalid is invalid",
+ "data": {"foo": 1, "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "both properties invalid is invalid",
+ "data": {"foo": [], "bar": {}},
+ "valid": false
+ },
+ {
+ "description": "doesn't invalidate other properties",
+ "data": {"quux": []},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description":
+ "properties, patternProperties, additionalProperties interaction",
+ "schema": {
+ "properties": {
+ "foo": {"type": "array", "maxItems": 3},
+ "bar": {"type": "array"}
+ },
+ "patternProperties": {"f.o": {"minItems": 2}},
+ "additionalProperties": {"type": "integer"}
+ },
+ "tests": [
+ {
+ "description": "property validates property",
+ "data": {"foo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "property invalidates property",
+ "data": {"foo": [1, 2, 3, 4]},
+ "valid": false
+ },
+ {
+ "description": "patternProperty invalidates property",
+ "data": {"foo": []},
+ "valid": false
+ },
+ {
+ "description": "patternProperty validates nonproperty",
+ "data": {"fxo": [1, 2]},
+ "valid": true
+ },
+ {
+ "description": "patternProperty invalidates nonproperty",
+ "data": {"fxo": []},
+ "valid": false
+ },
+ {
+ "description": "additionalProperty ignores property",
+ "data": {"bar": []},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty validates others",
+ "data": {"quux": 3},
+ "valid": true
+ },
+ {
+ "description": "additionalProperty invalidates others",
+ "data": {"quux": "foo"},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with boolean schema",
+ "schema": {
+ "properties": {
+ "foo": true,
+ "bar": false
+ }
+ },
+ "tests": [
+ {
+ "description": "no property present is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "only 'true' property present is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "only 'false' property present is invalid",
+ "data": {"bar": 2},
+ "valid": false
+ },
+ {
+ "description": "both properties present is invalid",
+ "data": {"foo": 1, "bar": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "properties with escaped characters",
+ "schema": {
+ "properties": {
+ "foo\nbar": {"type": "number"},
+ "foo\"bar": {"type": "number"},
+ "foo\\bar": {"type": "number"},
+ "foo\rbar": {"type": "number"},
+ "foo\tbar": {"type": "number"},
+ "foo\fbar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with all numbers is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1",
+ "foo\\bar": "1",
+ "foo\rbar": "1",
+ "foo\tbar": "1",
+ "foo\fbar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/propertyNames.json b/json/tests/draft7/propertyNames.json
new file mode 100644
index 0000000..f0788e6
--- /dev/null
+++ b/json/tests/draft7/propertyNames.json
@@ -0,0 +1,107 @@
+[
+ {
+ "description": "propertyNames validation",
+ "schema": {
+ "propertyNames": {"maxLength": 3}
+ },
+ "tests": [
+ {
+ "description": "all property names valid",
+ "data": {
+ "f": {},
+ "foo": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "some property names invalid",
+ "data": {
+ "foo": {},
+ "foobar": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "ignores arrays",
+ "data": [1, 2, 3, 4],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "foobar",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames validation with pattern",
+ "schema": {
+ "propertyNames": { "pattern": "^a+$" }
+ },
+ "tests": [
+ {
+ "description": "matching property names valid",
+ "data": {
+ "a": {},
+ "aa": {},
+ "aaa": {}
+ },
+ "valid": true
+ },
+ {
+ "description": "non-matching property name is invalid",
+ "data": {
+ "aaA": {}
+ },
+ "valid": false
+ },
+ {
+ "description": "object without properties is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema true",
+ "schema": {"propertyNames": true},
+ "tests": [
+ {
+ "description": "object with any properties is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "propertyNames with boolean schema false",
+ "schema": {"propertyNames": false},
+ "tests": [
+ {
+ "description": "object with any properties is invalid",
+ "data": {"foo": 1},
+ "valid": false
+ },
+ {
+ "description": "empty object is valid",
+ "data": {},
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/ref.json b/json/tests/draft7/ref.json
new file mode 100644
index 0000000..764d637
--- /dev/null
+++ b/json/tests/draft7/ref.json
@@ -0,0 +1,648 @@
+[
+ {
+ "description": "root pointer ref",
+ "schema": {
+ "properties": {
+ "foo": {"$ref": "#"}
+ },
+ "additionalProperties": false
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"foo": false},
+ "valid": true
+ },
+ {
+ "description": "recursive match",
+ "data": {"foo": {"foo": false}},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": false},
+ "valid": false
+ },
+ {
+ "description": "recursive mismatch",
+ "data": {"foo": {"bar": false}},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to object",
+ "schema": {
+ "properties": {
+ "foo": {"type": "integer"},
+ "bar": {"$ref": "#/properties/foo"}
+ }
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": {"bar": 3},
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": {"bar": true},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "relative pointer ref to array",
+ "schema": {
+ "items": [
+ {"type": "integer"},
+ {"$ref": "#/items/0"}
+ ]
+ },
+ "tests": [
+ {
+ "description": "match array",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "mismatch array",
+ "data": [1, "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "escaped pointer ref",
+ "schema": {
+ "definitions": {
+ "tilde~field": {"type": "integer"},
+ "slash/field": {"type": "integer"},
+ "percent%field": {"type": "integer"}
+ },
+ "properties": {
+ "tilde": {"$ref": "#/definitions/tilde~0field"},
+ "slash": {"$ref": "#/definitions/slash~1field"},
+ "percent": {"$ref": "#/definitions/percent%25field"}
+ }
+ },
+ "tests": [
+ {
+ "description": "slash invalid",
+ "data": {"slash": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "tilde invalid",
+ "data": {"tilde": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "percent invalid",
+ "data": {"percent": "aoeu"},
+ "valid": false
+ },
+ {
+ "description": "slash valid",
+ "data": {"slash": 123},
+ "valid": true
+ },
+ {
+ "description": "tilde valid",
+ "data": {"tilde": 123},
+ "valid": true
+ },
+ {
+ "description": "percent valid",
+ "data": {"percent": 123},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "nested refs",
+ "schema": {
+ "definitions": {
+ "a": {"type": "integer"},
+ "b": {"$ref": "#/definitions/a"},
+ "c": {"$ref": "#/definitions/b"}
+ },
+ "allOf": [{ "$ref": "#/definitions/c" }]
+ },
+ "tests": [
+ {
+ "description": "nested ref valid",
+ "data": 5,
+ "valid": true
+ },
+ {
+ "description": "nested ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref overrides any sibling keywords",
+ "schema": {
+ "definitions": {
+ "reffed": {
+ "type": "array"
+ }
+ },
+ "properties": {
+ "foo": {
+ "$ref": "#/definitions/reffed",
+ "maxItems": 2
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "ref valid",
+ "data": { "foo": [] },
+ "valid": true
+ },
+ {
+ "description": "ref valid, maxItems ignored",
+ "data": { "foo": [ 1, 2, 3] },
+ "valid": true
+ },
+ {
+ "description": "ref invalid",
+ "data": { "foo": "string" },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref prevents a sibling $id from changing the base uri",
+ "schema": {
+ "$id": "http://localhost:1234/sibling_id/base/",
+ "definitions": {
+ "foo": {
+ "$id": "http://localhost:1234/sibling_id/foo.json",
+ "type": "string"
+ },
+ "base_foo": {
+ "$comment": "this canonical uri is http://localhost:1234/sibling_id/base/foo.json",
+ "$id": "foo.json",
+ "type": "number"
+ }
+ },
+ "allOf": [
+ {
+ "$comment": "$ref resolves to http://localhost:1234/sibling_id/base/foo.json, not http://localhost:1234/sibling_id/foo.json",
+ "$id": "http://localhost:1234/sibling_id/",
+ "$ref": "foo.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "$ref resolves to /definitions/base_foo, data does not validate",
+ "data": "a",
+ "valid": false
+ },
+ {
+ "description": "$ref resolves to /definitions/base_foo, data validates",
+ "data": 1,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "remote ref, containing refs itself",
+ "schema": {"$ref": "http://json-schema.org/draft-07/schema#"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": {"minLength": 1},
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": {"minLength": -1},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref that is not a reference",
+ "schema": {
+ "properties": {
+ "$ref": {"type": "string"}
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "property named $ref, containing an actual $ref",
+ "schema": {
+ "properties": {
+ "$ref": {"$ref": "#/definitions/is-string"}
+ },
+ "definitions": {
+ "is-string": {
+ "type": "string"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "property named $ref valid",
+ "data": {"$ref": "a"},
+ "valid": true
+ },
+ {
+ "description": "property named $ref invalid",
+ "data": {"$ref": 2},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema true",
+ "schema": {
+ "allOf": [{ "$ref": "#/definitions/bool" }],
+ "definitions": {
+ "bool": true
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is valid",
+ "data": "foo",
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$ref to boolean schema false",
+ "schema": {
+ "allOf": [{ "$ref": "#/definitions/bool" }],
+ "definitions": {
+ "bool": false
+ }
+ },
+ "tests": [
+ {
+ "description": "any value is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Recursive references between schemas",
+ "schema": {
+ "$id": "http://localhost:1234/tree",
+ "description": "tree of nodes",
+ "type": "object",
+ "properties": {
+ "meta": {"type": "string"},
+ "nodes": {
+ "type": "array",
+ "items": {"$ref": "node"}
+ }
+ },
+ "required": ["meta", "nodes"],
+ "definitions": {
+ "node": {
+ "$id": "http://localhost:1234/node",
+ "description": "node",
+ "type": "object",
+ "properties": {
+ "value": {"type": "number"},
+ "subtree": {"$ref": "tree"}
+ },
+ "required": ["value"]
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "valid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 1.1},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": true
+ },
+ {
+ "description": "invalid tree",
+ "data": {
+ "meta": "root",
+ "nodes": [
+ {
+ "value": 1,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": "string is invalid"},
+ {"value": 1.2}
+ ]
+ }
+ },
+ {
+ "value": 2,
+ "subtree": {
+ "meta": "child",
+ "nodes": [
+ {"value": 2.1},
+ {"value": 2.2}
+ ]
+ }
+ }
+ ]
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "refs with quote",
+ "schema": {
+ "properties": {
+ "foo\"bar": {"$ref": "#/definitions/foo%22bar"}
+ },
+ "definitions": {
+ "foo\"bar": {"type": "number"}
+ }
+ },
+ "tests": [
+ {
+ "description": "object with numbers is valid",
+ "data": {
+ "foo\"bar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with strings is invalid",
+ "data": {
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier",
+ "schema": {
+ "allOf": [{
+ "$ref": "#foo"
+ }],
+ "definitions": {
+ "A": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "Location-independent identifier with base URI change in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/root",
+ "allOf": [{
+ "$ref": "http://localhost:1234/nested.json#foo"
+ }],
+ "definitions": {
+ "A": {
+ "$id": "nested.json",
+ "definitions": {
+ "B": {
+ "$id": "#foo",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "data": 1,
+ "description": "match",
+ "valid": true
+ },
+ {
+ "data": "a",
+ "description": "mismatch",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "naive replacement of $ref with its destination is not correct",
+ "schema": {
+ "definitions": {
+ "a_string": { "type": "string" }
+ },
+ "enum": [
+ { "$ref": "#/definitions/a_string" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "do not evaluate the $ref inside the enum, matching any string",
+ "data": "this is a string",
+ "valid": false
+ },
+ {
+ "description": "do not evaluate the $ref inside the enum, definition exact match",
+ "data": { "type": "string" },
+ "valid": false
+ },
+ {
+ "description": "match the enum exactly",
+ "data": { "$ref": "#/definitions/a_string" },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "refs with relative uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-relative-uri-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "schema-relative-uri-defs2.json",
+ "definitions": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "allOf": [ { "$ref": "#/definitions/inner" } ]
+ }
+ },
+ "allOf": [ { "$ref": "schema-relative-uri-defs2.json" } ]
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "relative refs with absolute uris and defs",
+ "schema": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs1.json",
+ "properties": {
+ "foo": {
+ "$id": "http://example.com/schema-refs-absolute-uris-defs2.json",
+ "definitions": {
+ "inner": {
+ "properties": {
+ "bar": { "type": "string" }
+ }
+ }
+ },
+ "allOf": [ { "$ref": "#/definitions/inner" } ]
+ }
+ },
+ "allOf": [ { "$ref": "schema-refs-absolute-uris-defs2.json" } ]
+ },
+ "tests": [
+ {
+ "description": "invalid on inner field",
+ "data": {
+ "foo": {
+ "bar": 1
+ },
+ "bar": "a"
+ },
+ "valid": false
+ },
+ {
+ "description": "invalid on outer field",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid on both fields",
+ "data": {
+ "foo": {
+ "bar": "a"
+ },
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "$id must be resolved against nearest parent, not just immediate parent",
+ "schema": {
+ "$id": "http://example.com/a.json",
+ "definitions": {
+ "x": {
+ "$id": "http://example.com/b/c.json",
+ "not": {
+ "definitions": {
+ "y": {
+ "$id": "d.json",
+ "type": "number"
+ }
+ }
+ }
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "http://example.com/b/d.json"
+ }
+ ]
+ },
+ "tests": [
+ {
+ "description": "number should pass",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number should fail",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/refRemote.json b/json/tests/draft7/refRemote.json
new file mode 100644
index 0000000..a2221b2
--- /dev/null
+++ b/json/tests/draft7/refRemote.json
@@ -0,0 +1,196 @@
+[
+ {
+ "description": "remote ref",
+ "schema": {"$ref": "http://localhost:1234/integer.json"},
+ "tests": [
+ {
+ "description": "remote ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "fragment within remote ref",
+ "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"},
+ "tests": [
+ {
+ "description": "remote fragment valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "remote fragment invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "ref within remote ref",
+ "schema": {
+ "$ref": "http://localhost:1234/subSchemas.json#/refToInteger"
+ },
+ "tests": [
+ {
+ "description": "ref within ref valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "ref within ref invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change",
+ "schema": {
+ "$id": "http://localhost:1234/",
+ "items": {
+ "$id": "baseUriChange/",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ },
+ "tests": [
+ {
+ "description": "base URI change ref valid",
+ "data": [[1]],
+ "valid": true
+ },
+ {
+ "description": "base URI change ref invalid",
+ "data": [["a"]],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs1.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz"}
+ },
+ "definitions": {
+ "baz": {
+ "$id": "baseUriChangeFolder/",
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "base URI change - change folder in subschema",
+ "schema": {
+ "$id": "http://localhost:1234/scope_change_defs2.json",
+ "type" : "object",
+ "properties": {
+ "list": {"$ref": "#/definitions/baz/definitions/bar"}
+ },
+ "definitions": {
+ "baz": {
+ "$id": "baseUriChangeFolderInSubschema/",
+ "definitions": {
+ "bar": {
+ "type": "array",
+ "items": {"$ref": "folderInteger.json"}
+ }
+ }
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": {"list": [1]},
+ "valid": true
+ },
+ {
+ "description": "string is invalid",
+ "data": {"list": ["a"]},
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "root ref in remote ref",
+ "schema": {
+ "$id": "http://localhost:1234/object",
+ "type": "object",
+ "properties": {
+ "name": {"$ref": "name.json#/definitions/orNull"}
+ }
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": {
+ "name": "foo"
+ },
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": {
+ "name": null
+ },
+ "valid": true
+ },
+ {
+ "description": "object is invalid",
+ "data": {
+ "name": {
+ "name": null
+ }
+ },
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "remote ref with ref to definitions",
+ "schema": {
+ "$id": "http://localhost:1234/schema-remote-ref-ref-defs1.json",
+ "allOf": [
+ { "$ref": "ref-and-definitions.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "invalid",
+ "data": {
+ "bar": 1
+ },
+ "valid": false
+ },
+ {
+ "description": "valid",
+ "data": {
+ "bar": "a"
+ },
+ "valid": true
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/required.json b/json/tests/draft7/required.json
new file mode 100644
index 0000000..abf18f3
--- /dev/null
+++ b/json/tests/draft7/required.json
@@ -0,0 +1,105 @@
+[
+ {
+ "description": "required validation",
+ "schema": {
+ "properties": {
+ "foo": {},
+ "bar": {}
+ },
+ "required": ["foo"]
+ },
+ "tests": [
+ {
+ "description": "present required property is valid",
+ "data": {"foo": 1},
+ "valid": true
+ },
+ {
+ "description": "non-present required property is invalid",
+ "data": {"bar": 1},
+ "valid": false
+ },
+ {
+ "description": "ignores arrays",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "ignores strings",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "ignores other non-objects",
+ "data": 12,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required default validation",
+ "schema": {
+ "properties": {
+ "foo": {}
+ }
+ },
+ "tests": [
+ {
+ "description": "not required by default",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with empty array",
+ "schema": {
+ "properties": {
+ "foo": {}
+ },
+ "required": []
+ },
+ "tests": [
+ {
+ "description": "property not required",
+ "data": {},
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "required with escaped characters",
+ "schema": {
+ "required": [
+ "foo\nbar",
+ "foo\"bar",
+ "foo\\bar",
+ "foo\rbar",
+ "foo\tbar",
+ "foo\fbar"
+ ]
+ },
+ "tests": [
+ {
+ "description": "object with all properties present is valid",
+ "data": {
+ "foo\nbar": 1,
+ "foo\"bar": 1,
+ "foo\\bar": 1,
+ "foo\rbar": 1,
+ "foo\tbar": 1,
+ "foo\fbar": 1
+ },
+ "valid": true
+ },
+ {
+ "description": "object with some properties missing is invalid",
+ "data": {
+ "foo\nbar": "1",
+ "foo\"bar": "1"
+ },
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/type.json b/json/tests/draft7/type.json
new file mode 100644
index 0000000..8304647
--- /dev/null
+++ b/json/tests/draft7/type.json
@@ -0,0 +1,474 @@
+[
+ {
+ "description": "integer type matches integers",
+ "schema": {"type": "integer"},
+ "tests": [
+ {
+ "description": "an integer is an integer",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is an integer",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is not an integer",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an integer",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not an integer, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not an integer",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not an integer",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an integer",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an integer",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "number type matches numbers",
+ "schema": {"type": "number"},
+ "tests": [
+ {
+ "description": "an integer is a number",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a float with zero fractional part is a number (and an integer)",
+ "data": 1.0,
+ "valid": true
+ },
+ {
+ "description": "a float is a number",
+ "data": 1.1,
+ "valid": true
+ },
+ {
+ "description": "a string is not a number",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "a string is still not a number, even if it looks like one",
+ "data": "1",
+ "valid": false
+ },
+ {
+ "description": "an object is not a number",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a number",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a number",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a number",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "string type matches strings",
+ "schema": {"type": "string"},
+ "tests": [
+ {
+ "description": "1 is not a string",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not a string",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is a string",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a string is still a string, even if it looks like a number",
+ "data": "1",
+ "valid": true
+ },
+ {
+ "description": "an empty string is still a string",
+ "data": "",
+ "valid": true
+ },
+ {
+ "description": "an object is not a string",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a string",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not a string",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not a string",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "object type matches objects",
+ "schema": {"type": "object"},
+ "tests": [
+ {
+ "description": "an integer is not an object",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an object",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an object",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is an object",
+ "data": {},
+ "valid": true
+ },
+ {
+ "description": "an array is not an object",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is not an object",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an object",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "array type matches arrays",
+ "schema": {"type": "array"},
+ "tests": [
+ {
+ "description": "an integer is not an array",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not an array",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not an array",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an object is not an array",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is an array",
+ "data": [],
+ "valid": true
+ },
+ {
+ "description": "a boolean is not an array",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is not an array",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "boolean type matches booleans",
+ "schema": {"type": "boolean"},
+ "tests": [
+ {
+ "description": "an integer is not a boolean",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "zero is not a boolean",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a float is not a boolean",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "a string is not a boolean",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not a boolean",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not a boolean",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not a boolean",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is a boolean",
+ "data": true,
+ "valid": true
+ },
+ {
+ "description": "false is a boolean",
+ "data": false,
+ "valid": true
+ },
+ {
+ "description": "null is not a boolean",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "null type matches only the null object",
+ "schema": {"type": "null"},
+ "tests": [
+ {
+ "description": "an integer is not null",
+ "data": 1,
+ "valid": false
+ },
+ {
+ "description": "a float is not null",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "zero is not null",
+ "data": 0,
+ "valid": false
+ },
+ {
+ "description": "a string is not null",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "an empty string is not null",
+ "data": "",
+ "valid": false
+ },
+ {
+ "description": "an object is not null",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is not null",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "true is not null",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "false is not null",
+ "data": false,
+ "valid": false
+ },
+ {
+ "description": "null is null",
+ "data": null,
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "multiple types can be specified in an array",
+ "schema": {"type": ["integer", "string"]},
+ "tests": [
+ {
+ "description": "an integer is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "a string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "a float is invalid",
+ "data": 1.1,
+ "valid": false
+ },
+ {
+ "description": "an object is invalid",
+ "data": {},
+ "valid": false
+ },
+ {
+ "description": "an array is invalid",
+ "data": [],
+ "valid": false
+ },
+ {
+ "description": "a boolean is invalid",
+ "data": true,
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type as array with one item",
+ "schema": {
+ "type": ["string"]
+ },
+ "tests": [
+ {
+ "description": "string is valid",
+ "data": "foo",
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array or object",
+ "schema": {
+ "type": ["array", "object"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ },
+ {
+ "description": "null is invalid",
+ "data": null,
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "type: array, object or null",
+ "schema": {
+ "type": ["array", "object", "null"]
+ },
+ "tests": [
+ {
+ "description": "array is valid",
+ "data": [1,2,3],
+ "valid": true
+ },
+ {
+ "description": "object is valid",
+ "data": {"foo": 123},
+ "valid": true
+ },
+ {
+ "description": "null is valid",
+ "data": null,
+ "valid": true
+ },
+ {
+ "description": "number is invalid",
+ "data": 123,
+ "valid": false
+ },
+ {
+ "description": "string is invalid",
+ "data": "foo",
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/uniqueItems.json b/json/tests/draft7/uniqueItems.json
new file mode 100644
index 0000000..2ccf666
--- /dev/null
+++ b/json/tests/draft7/uniqueItems.json
@@ -0,0 +1,404 @@
+[
+ {
+ "description": "uniqueItems validation",
+ "schema": {"uniqueItems": true},
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is invalid",
+ "data": [1, 1],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two integers is invalid",
+ "data": [1, 2, 1],
+ "valid": false
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": false
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of strings is valid",
+ "data": ["foo", "bar", "baz"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of strings is invalid",
+ "data": ["foo", "bar", "foo"],
+ "valid": false
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is invalid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": false
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is invalid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": false
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is invalid",
+ "data": [["foo"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "non-unique array of more than two arrays is invalid",
+ "data": [["foo"], ["bar"], ["foo"]],
+ "valid": false
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "[1] and [true] are unique",
+ "data": [[1], [true]],
+ "valid": true
+ },
+ {
+ "description": "[0] and [false] are unique",
+ "data": [[0], [false]],
+ "valid": true
+ },
+ {
+ "description": "nested [1] and [true] are unique",
+ "data": [[[1], "foo"], [[true], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "nested [0] and [false] are unique",
+ "data": [[[0], "foo"], [[false], "foo"]],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1, "{}"],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are invalid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": false
+ },
+ {
+ "description": "different objects are unique",
+ "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}],
+ "valid": true
+ },
+ {
+ "description": "objects are non-unique despite key order",
+ "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}],
+ "valid": false
+ },
+ {
+ "description": "{\"a\": false} and {\"a\": 0} are unique",
+ "data": [{"a": false}, {"a": 0}],
+ "valid": true
+ },
+ {
+ "description": "{\"a\": true} and {\"a\": 1} are unique",
+ "data": [{"a": true}, {"a": 1}],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is not valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": false
+ },
+ {
+ "description": "non-unique array extended from [true, false] is not valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": true,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is not valid",
+ "data": [false, false],
+ "valid": false
+ },
+ {
+ "description": "[true, true] from items array is not valid",
+ "data": [true, true],
+ "valid": false
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false validation",
+ "schema": { "uniqueItems": false },
+ "tests": [
+ {
+ "description": "unique array of integers is valid",
+ "data": [1, 2],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of integers is valid",
+ "data": [1, 1],
+ "valid": true
+ },
+ {
+ "description": "numbers are unique if mathematically unequal",
+ "data": [1.0, 1.00, 1],
+ "valid": true
+ },
+ {
+ "description": "false is not equal to zero",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "true is not equal to one",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "baz"}],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of objects is valid",
+ "data": [{"foo": "bar"}, {"foo": "bar"}],
+ "valid": true
+ },
+ {
+ "description": "unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : false}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of nested objects is valid",
+ "data": [
+ {"foo": {"bar" : {"baz" : true}}},
+ {"foo": {"bar" : {"baz" : true}}}
+ ],
+ "valid": true
+ },
+ {
+ "description": "unique array of arrays is valid",
+ "data": [["foo"], ["bar"]],
+ "valid": true
+ },
+ {
+ "description": "non-unique array of arrays is valid",
+ "data": [["foo"], ["foo"]],
+ "valid": true
+ },
+ {
+ "description": "1 and true are unique",
+ "data": [1, true],
+ "valid": true
+ },
+ {
+ "description": "0 and false are unique",
+ "data": [0, false],
+ "valid": true
+ },
+ {
+ "description": "unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, 1],
+ "valid": true
+ },
+ {
+ "description": "non-unique heterogeneous types are valid",
+ "data": [{}, [1], true, null, {}, 1],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "bar"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [false, true] is valid",
+ "data": [false, true, "foo", "foo"],
+ "valid": true
+ },
+ {
+ "description": "non-unique array extended from [true, false] is valid",
+ "data": [true, false, "foo", "foo"],
+ "valid": true
+ }
+ ]
+ },
+ {
+ "description": "uniqueItems=false with an array of items and additionalItems=false",
+ "schema": {
+ "items": [{"type": "boolean"}, {"type": "boolean"}],
+ "uniqueItems": false,
+ "additionalItems": false
+ },
+ "tests": [
+ {
+ "description": "[false, true] from items array is valid",
+ "data": [false, true],
+ "valid": true
+ },
+ {
+ "description": "[true, false] from items array is valid",
+ "data": [true, false],
+ "valid": true
+ },
+ {
+ "description": "[false, false] from items array is valid",
+ "data": [false, false],
+ "valid": true
+ },
+ {
+ "description": "[true, true] from items array is valid",
+ "data": [true, true],
+ "valid": true
+ },
+ {
+ "description": "extra items are invalid even if unique",
+ "data": [false, true, null],
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/draft7/unknownKeyword.json b/json/tests/draft7/unknownKeyword.json
new file mode 100644
index 0000000..1f58d97
--- /dev/null
+++ b/json/tests/draft7/unknownKeyword.json
@@ -0,0 +1,56 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "definitions": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/definitions/id_in_unknown0" },
+ { "$ref": "#/definitions/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/json/tests/latest b/json/tests/latest
new file mode 120000
index 0000000..9a4784d
--- /dev/null
+++ b/json/tests/latest
@@ -0,0 +1 @@
+draft2020-12 \ No newline at end of file
diff --git a/json/tox.ini b/json/tox.ini
new file mode 100644
index 0000000..a6c860a
--- /dev/null
+++ b/json/tox.ini
@@ -0,0 +1,9 @@
+[tox]
+minversion = 1.6
+envlist = sanity
+skipsdist = True
+
+[testenv:sanity]
+# used just for validating the structure of the test case files themselves
+deps = jsonschema==3.2.0
+commands = {envpython} bin/jsonschema_suite check