New upstream version 1.2.0
Shengjing Zhu
4 years ago
0 | 0 | language: go |
1 | 1 | go: |
2 | - 1.3 | |
2 | - "1.11" | |
3 | - "1.12" | |
4 | - "1.13" | |
3 | 5 | before_install: |
4 | - go get github.com/sigu-399/gojsonreference | |
5 | - go get github.com/sigu-399/gojsonpointer | |
6 | - go get github.com/xeipuuv/gojsonreference | |
7 | - go get github.com/xeipuuv/gojsonpointer | |
6 | 8 | - go get github.com/stretchr/testify/assert |
0 | [![GoDoc](https://godoc.org/github.com/xeipuuv/gojsonschema?status.svg)](https://godoc.org/github.com/xeipuuv/gojsonschema) | |
0 | 1 | [![Build Status](https://travis-ci.org/xeipuuv/gojsonschema.svg)](https://travis-ci.org/xeipuuv/gojsonschema) |
2 | [![Go Report Card](https://goreportcard.com/badge/github.com/xeipuuv/gojsonschema)](https://goreportcard.com/report/github.com/xeipuuv/gojsonschema) | |
1 | 3 | |
2 | 4 | # gojsonschema |
3 | 5 | |
4 | 6 | ## Description |
5 | 7 | |
6 | An implementation of JSON Schema, based on IETF's draft v4 - Go language | |
8 | An implementation of JSON Schema for the Go programming language. Supports draft-04, draft-06 and draft-07. | |
7 | 9 | |
8 | 10 | References : |
9 | 11 | |
53 | 55 | fmt.Printf("- %s\n", desc) |
54 | 56 | } |
55 | 57 | } |
56 | ||
57 | 58 | } |
58 | 59 | |
59 | 60 | |
147 | 148 | } |
148 | 149 | ``` |
149 | 150 | |
151 | ||
152 | ## Loading local schemas | |
153 | ||
154 | By default `file` and `http(s)` references to external schemas are loaded automatically via the file system or via http(s). An external schema can also be loaded using a `SchemaLoader`. | |
155 | ||
156 | ```go | |
157 | sl := gojsonschema.NewSchemaLoader() | |
158 | loader1 := gojsonschema.NewStringLoader(`{ "type" : "string" }`) | |
159 | err := sl.AddSchema("http://some_host.com/string.json", loader1) | |
160 | ``` | |
161 | ||
162 | Alternatively if your schema already has an `$id` you can use the `AddSchemas` function | |
163 | ```go | |
164 | loader2 := gojsonschema.NewStringLoader(`{ | |
165 | "$id" : "http://some_host.com/maxlength.json", | |
166 | "maxLength" : 5 | |
167 | }`) | |
168 | err = sl.AddSchemas(loader2) | |
169 | ``` | |
170 | ||
171 | The main schema should be passed to the `Compile` function. This main schema can then directly reference the added schemas without needing to download them. | |
172 | ```go | |
173 | loader3 := gojsonschema.NewStringLoader(`{ | |
174 | "$id" : "http://some_host.com/main.json", | |
175 | "allOf" : [ | |
176 | { "$ref" : "http://some_host.com/string.json" }, | |
177 | { "$ref" : "http://some_host.com/maxlength.json" } | |
178 | ] | |
179 | }`) | |
180 | ||
181 | schema, err := sl.Compile(loader3) | |
182 | ||
183 | documentLoader := gojsonschema.NewStringLoader(`"hello world"`) | |
184 | ||
185 | result, err := schema.Validate(documentLoader) | |
186 | ``` | |
187 | ||
188 | It's also possible to pass a `ReferenceLoader` to the `Compile` function that references a loaded schema. | |
189 | ||
190 | ```go | |
191 | err = sl.AddSchemas(loader3) | |
192 | schema, err := sl.Compile(gojsonschema.NewReferenceLoader("http://some_host.com/main.json")) | |
193 | ``` | |
194 | ||
195 | Schemas added by `AddSchema` and `AddSchemas` are only validated when the entire schema is compiled, unless meta-schema validation is used. | |
196 | ||
197 | ## Using a specific draft | |
198 | By default `gojsonschema` will try to detect the draft of a schema by using the `$schema` keyword and parse it in a strict draft-04, draft-06 or draft-07 mode. If `$schema` is missing, or the draft version is not explicitely set, a hybrid mode is used which merges together functionality of all drafts into one mode. | |
199 | ||
200 | Autodectection can be turned off with the `AutoDetect` property. Specific draft versions can be specified with the `Draft` property. | |
201 | ||
202 | ```go | |
203 | sl := gojsonschema.NewSchemaLoader() | |
204 | sl.Draft = gojsonschema.Draft7 | |
205 | sl.AutoDetect = false | |
206 | ``` | |
207 | ||
208 | If autodetection is on (default), a draft-07 schema can savely reference draft-04 schemas and vice-versa, as long as `$schema` is specified in all schemas. | |
209 | ||
210 | ## Meta-schema validation | |
211 | Schemas that are added using the `AddSchema`, `AddSchemas` and `Compile` can be validated against their meta-schema by setting the `Validate` property. | |
212 | ||
213 | The following example will produce an error as `multipleOf` must be a number. If `Validate` is off (default), this error is only returned at the `Compile` step. | |
214 | ||
215 | ```go | |
216 | sl := gojsonschema.NewSchemaLoader() | |
217 | sl.Validate = true | |
218 | err := sl.AddSchemas(gojsonschema.NewStringLoader(`{ | |
219 | $id" : "http://some_host.com/invalid.json", | |
220 | "$schema": "http://json-schema.org/draft-07/schema#", | |
221 | "multipleOf" : true | |
222 | }`)) | |
223 | ``` | |
224 | ``` | |
225 | ``` | |
226 | ||
227 | Errors returned by meta-schema validation are more readable and contain more information, which helps significantly if you are developing a schema. | |
228 | ||
229 | Meta-schema validation also works with a custom `$schema`. In case `$schema` is missing, or `AutoDetect` is set to `false`, the meta-schema of the used draft is used. | |
230 | ||
231 | ||
150 | 232 | ## Working with Errors |
151 | 233 | |
152 | 234 | The library handles string error codes which you can customize by creating your own gojsonschema.locale and setting it |
154 | 236 | gojsonschema.Locale = YourCustomLocale{} |
155 | 237 | ``` |
156 | 238 | |
157 | However, each error contains additional contextual information. | |
239 | However, each error contains additional contextual information. | |
240 | ||
241 | Newer versions of `gojsonschema` may have new additional errors, so code that uses a custom locale will need to be updated when this happens. | |
158 | 242 | |
159 | 243 | **err.Type()**: *string* Returns the "type" of error that occurred. Note you can also type check. See below |
160 | 244 | |
168 | 252 | "number_not": NumberNotError |
169 | 253 | "missing_dependency": MissingDependencyError |
170 | 254 | "internal": InternalError |
255 | "const": ConstEror | |
171 | 256 | "enum": EnumError |
172 | 257 | "array_no_additional_items": ArrayNoAdditionalItemsError |
173 | 258 | "array_min_items": ArrayMinItemsError |
174 | 259 | "array_max_items": ArrayMaxItemsError |
175 | 260 | "unique": ItemsMustBeUniqueError |
261 | "contains" : ArrayContainsError | |
176 | 262 | "array_min_properties": ArrayMinPropertiesError |
177 | 263 | "array_max_properties": ArrayMaxPropertiesError |
178 | 264 | "additional_property_not_allowed": AdditionalPropertyNotAllowedError |
179 | 265 | "invalid_property_pattern": InvalidPropertyPatternError |
266 | "invalid_property_name": InvalidPropertyNameError | |
180 | 267 | "string_gte": StringLengthGTEError |
181 | 268 | "string_lte": StringLengthLTEError |
182 | 269 | "pattern": DoesNotMatchPatternError |
185 | 272 | "number_gt": NumberGTError |
186 | 273 | "number_lte": NumberLTEError |
187 | 274 | "number_lt": NumberLTError |
275 | "condition_then" : ConditionThenError | |
276 | "condition_else" : ConditionElseError | |
188 | 277 | |
189 | 278 | **err.Value()**: *interface{}* Returns the value given |
190 | 279 | |
191 | **err.Context()**: *gojsonschema.jsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName | |
280 | **err.Context()**: *gojsonschema.JsonContext* Returns the context. This has a String() method that will print something like this: (root).firstName | |
192 | 281 | |
193 | 282 | **err.Field()**: *string* Returns the fieldname in the format firstName, or for embedded properties, person.firstName. This returns the same as the String() method on *err.Context()* but removes the (root). prefix. |
194 | 283 | |
195 | 284 | **err.Description()**: *string* The error description. This is based on the locale you are using. See the beginning of this section for overwriting the locale with a custom implementation. |
196 | 285 | |
286 | **err.DescriptionFormat()**: *string* The error description format. This is relevant if you are adding custom validation errors afterwards to the result. | |
287 | ||
197 | 288 | **err.Details()**: *gojsonschema.ErrorDetails* Returns a map[string]interface{} of additional error details specific to the error. For example, GTE errors will have a "min" value, LTE will have a "max" value. See errors.go for a full description of all the error details. Every error always contains a "field" key that holds the value of *err.Field()* |
198 | 289 | |
199 | 290 | Note in most cases, the err.Details() will be used to generate replacement strings in your locales, and not used directly. These strings follow the text/template format i.e. |
201 | 292 | {{.field}} must be greater than or equal to {{.min}} |
202 | 293 | ``` |
203 | 294 | |
295 | The library allows you to specify custom template functions, should you require more complex error message handling. | |
296 | ```go | |
297 | gojsonschema.ErrorTemplateFuncs = map[string]interface{}{ | |
298 | "allcaps": func(s string) string { | |
299 | return strings.ToUpper(s) | |
300 | }, | |
301 | } | |
302 | ``` | |
303 | ||
304 | Given the above definition, you can use the custom function `"allcaps"` in your localization templates: | |
305 | ``` | |
306 | {{allcaps .field}} must be greater than or equal to {{.min}} | |
307 | ``` | |
308 | ||
309 | The above error message would then be rendered with the `field` value in capital letters. For example: | |
310 | ``` | |
311 | "PASSWORD must be greater than or equal to 8" | |
312 | ``` | |
313 | ||
314 | Learn more about what types of template functions you can use in `ErrorTemplateFuncs` by referring to Go's [text/template FuncMap](https://golang.org/pkg/text/template/#FuncMap) type. | |
315 | ||
204 | 316 | ## Formats |
205 | JSON Schema allows for optional "format" property to validate strings against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this: | |
317 | JSON Schema allows for optional "format" property to validate instances against well-known formats. gojsonschema ships with all of the formats defined in the spec that you can use like this: | |
318 | ||
206 | 319 | ````json |
207 | 320 | {"type": "string", "format": "email"} |
208 | 321 | ```` |
209 | Available formats: date-time, hostname, email, ipv4, ipv6, uri. | |
322 | ||
323 | Not all formats defined in draft-07 are available. Implemented formats are: | |
324 | ||
325 | * `date` | |
326 | * `time` | |
327 | * `date-time` | |
328 | * `hostname`. Subdomains that start with a number are also supported, but this means that it doesn't strictly follow [RFC1034](http://tools.ietf.org/html/rfc1034#section-3.5) and has the implication that ipv4 addresses are also recognized as valid hostnames. | |
329 | * `email`. Go's email parser deviates slightly from [RFC5322](https://tools.ietf.org/html/rfc5322). Includes unicode support. | |
330 | * `idn-email`. Same caveat as `email`. | |
331 | * `ipv4` | |
332 | * `ipv6` | |
333 | * `uri`. Includes unicode support. | |
334 | * `uri-reference`. Includes unicode support. | |
335 | * `iri` | |
336 | * `iri-reference` | |
337 | * `uri-template` | |
338 | * `uuid` | |
339 | * `regex`. Go uses the [RE2](https://github.com/google/re2/wiki/Syntax) engine and is not [ECMA262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf) compatible. | |
340 | * `json-pointer` | |
341 | * `relative-json-pointer` | |
342 | ||
343 | `email`, `uri` and `uri-reference` use the same validation code as their unicode counterparts `idn-email`, `iri` and `iri-reference`. If you rely on unicode support you should use the specific | |
344 | unicode enabled formats for the sake of interoperability as other implementations might not support unicode in the regular formats. | |
345 | ||
346 | The validation code for `uri`, `idn-email` and their relatives use mostly standard library code. | |
210 | 347 | |
211 | 348 | For repetitive or more complex formats, you can create custom format checkers and add them to gojsonschema like this: |
212 | 349 | |
215 | 352 | type RoleFormatChecker struct {} |
216 | 353 | |
217 | 354 | // Ensure it meets the gojsonschema.FormatChecker interface |
218 | func (f RoleFormatChecker) IsFormat(input string) bool { | |
219 | return strings.HasPrefix("ROLE_", input) | |
355 | func (f RoleFormatChecker) IsFormat(input interface{}) bool { | |
356 | ||
357 | asString, ok := input.(string) | |
358 | if ok == false { | |
359 | return false | |
360 | } | |
361 | ||
362 | return strings.HasPrefix("ROLE_", asString) | |
220 | 363 | } |
221 | 364 | |
222 | 365 | // Add it to the library |
228 | 371 | {"type": "string", "format": "role"} |
229 | 372 | ```` |
230 | 373 | |
374 | Another example would be to check if the provided integer matches an id on database: | |
375 | ||
376 | JSON schema: | |
377 | ```json | |
378 | {"type": "integer", "format": "ValidUserId"} | |
379 | ``` | |
380 | ||
381 | ```go | |
382 | // Define the format checker | |
383 | type ValidUserIdFormatChecker struct {} | |
384 | ||
385 | // Ensure it meets the gojsonschema.FormatChecker interface | |
386 | func (f ValidUserIdFormatChecker) IsFormat(input interface{}) bool { | |
387 | ||
388 | asFloat64, ok := input.(float64) // Numbers are always float64 here | |
389 | if ok == false { | |
390 | return false | |
391 | } | |
392 | ||
393 | // XXX | |
394 | // do the magic on the database looking for the int(asFloat64) | |
395 | ||
396 | return true | |
397 | } | |
398 | ||
399 | // Add it to the library | |
400 | gojsonschema.FormatCheckers.Add("ValidUserId", ValidUserIdFormatChecker{}) | |
401 | ```` | |
402 | ||
403 | Formats can also be removed, for example if you want to override one of the formats that is defined by default. | |
404 | ||
405 | ```go | |
406 | gojsonschema.FormatCheckers.Remove("hostname") | |
407 | ``` | |
408 | ||
409 | ||
410 | ## Additional custom validation | |
411 | After the validation has run and you have the results, you may add additional | |
412 | errors using `Result.AddError`. This is useful to maintain the same format within the resultset instead | |
413 | of having to add special exceptions for your own errors. Below is an example. | |
414 | ||
415 | ```go | |
416 | type AnswerInvalidError struct { | |
417 | gojsonschema.ResultErrorFields | |
418 | } | |
419 | ||
420 | func newAnswerInvalidError(context *gojsonschema.JsonContext, value interface{}, details gojsonschema.ErrorDetails) *AnswerInvalidError { | |
421 | err := AnswerInvalidError{} | |
422 | err.SetContext(context) | |
423 | err.SetType("custom_invalid_error") | |
424 | // it is important to use SetDescriptionFormat() as this is used to call SetDescription() after it has been parsed | |
425 | // using the description of err will be overridden by this. | |
426 | err.SetDescriptionFormat("Answer to the Ultimate Question of Life, the Universe, and Everything is {{.answer}}") | |
427 | err.SetValue(value) | |
428 | err.SetDetails(details) | |
429 | ||
430 | return &err | |
431 | } | |
432 | ||
433 | func main() { | |
434 | // ... | |
435 | schema, err := gojsonschema.NewSchema(schemaLoader) | |
436 | result, err := gojsonschema.Validate(schemaLoader, documentLoader) | |
437 | ||
438 | if true { // some validation | |
439 | jsonContext := gojsonschema.NewJsonContext("question", nil) | |
440 | errDetail := gojsonschema.ErrorDetails{ | |
441 | "answer": 42, | |
442 | } | |
443 | result.AddError( | |
444 | newAnswerInvalidError( | |
445 | gojsonschema.NewJsonContext("answer", jsonContext), | |
446 | 52, | |
447 | errDetail, | |
448 | ), | |
449 | errDetail, | |
450 | ) | |
451 | } | |
452 | ||
453 | return result, err | |
454 | ||
455 | } | |
456 | ``` | |
457 | ||
458 | This is especially useful if you want to add validation beyond what the | |
459 | json schema drafts can provide such business specific logic. | |
460 | ||
231 | 461 | ## Uses |
232 | 462 | |
233 | 463 | gojsonschema uses the following test suite : |
0 | // Copyright 2018 johandorland ( https://github.com/johandorland ) | |
1 | // | |
2 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | // you may not use this file except in compliance with the License. | |
4 | // You may obtain a copy of the License at | |
5 | // | |
6 | // http://www.apache.org/licenses/LICENSE-2.0 | |
7 | // | |
8 | // Unless required by applicable law or agreed to in writing, software | |
9 | // distributed under the License is distributed on an "AS IS" BASIS, | |
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | // See the License for the specific language governing permissions and | |
12 | // limitations under the License. | |
13 | ||
14 | package gojsonschema | |
15 | ||
16 | import ( | |
17 | "errors" | |
18 | "math" | |
19 | "reflect" | |
20 | ||
21 | "github.com/xeipuuv/gojsonreference" | |
22 | ) | |
23 | ||
24 | // Draft is a JSON-schema draft version | |
25 | type Draft int | |
26 | ||
27 | // Supported Draft versions | |
28 | const ( | |
29 | Draft4 Draft = 4 | |
30 | Draft6 Draft = 6 | |
31 | Draft7 Draft = 7 | |
32 | Hybrid Draft = math.MaxInt32 | |
33 | ) | |
34 | ||
35 | type draftConfig struct { | |
36 | Version Draft | |
37 | MetaSchemaURL string | |
38 | MetaSchema string | |
39 | } | |
40 | type draftConfigs []draftConfig | |
41 | ||
42 | var drafts draftConfigs | |
43 | ||
44 | func init() { | |
45 | drafts = []draftConfig{ | |
46 | { | |
47 | Version: Draft4, | |
48 | MetaSchemaURL: "http://json-schema.org/draft-04/schema", | |
49 | MetaSchema: `{"id":"http://json-schema.org/draft-04/schema#","$schema":"http://json-schema.org/draft-04/schema#","description":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"positiveInteger":{"type":"integer","minimum":0},"positiveIntegerDefault0":{"allOf":[{"$ref":"#/definitions/positiveInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"minItems":1,"uniqueItems":true}},"type":"object","properties":{"id":{"type":"string"},"$schema":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"multipleOf":{"type":"number","minimum":0,"exclusiveMinimum":true},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"boolean","default":false},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"boolean","default":false},"maxLength":{"$ref":"#/definitions/positiveInteger"},"minLength":{"$ref":"#/definitions/positiveIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/positiveInteger"},"minItems":{"$ref":"#/definitions/positiveIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"maxProperties":{"$ref":"#/definitions/positiveInteger"},"minProperties":{"$ref":"#/definitions/positiveIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"anyOf":[{"type":"boolean"},{"$ref":"#"}],"default":{}},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"dependencies":{"exclusiveMaximum":["maximum"],"exclusiveMinimum":["minimum"]},"default":{}}`, | |
50 | }, | |
51 | { | |
52 | Version: Draft6, | |
53 | MetaSchemaURL: "http://json-schema.org/draft-06/schema", | |
54 | MetaSchema: `{"$schema":"http://json-schema.org/draft-06/schema#","$id":"http://json-schema.org/draft-06/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"title":{"type":"string"},"description":{"type":"string"},"default":{},"examples":{"type":"array","items":{}},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":{}},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":{},"enum":{"type":"array","minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":{}}`, | |
55 | }, | |
56 | { | |
57 | Version: Draft7, | |
58 | MetaSchemaURL: "http://json-schema.org/draft-07/schema", | |
59 | MetaSchema: `{"$schema":"http://json-schema.org/draft-07/schema#","$id":"http://json-schema.org/draft-07/schema#","title":"Core schema meta-schema","definitions":{"schemaArray":{"type":"array","minItems":1,"items":{"$ref":"#"}},"nonNegativeInteger":{"type":"integer","minimum":0},"nonNegativeIntegerDefault0":{"allOf":[{"$ref":"#/definitions/nonNegativeInteger"},{"default":0}]},"simpleTypes":{"enum":["array","boolean","integer","null","number","object","string"]},"stringArray":{"type":"array","items":{"type":"string"},"uniqueItems":true,"default":[]}},"type":["object","boolean"],"properties":{"$id":{"type":"string","format":"uri-reference"},"$schema":{"type":"string","format":"uri"},"$ref":{"type":"string","format":"uri-reference"},"$comment":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"default":true,"readOnly":{"type":"boolean","default":false},"examples":{"type":"array","items":true},"multipleOf":{"type":"number","exclusiveMinimum":0},"maximum":{"type":"number"},"exclusiveMaximum":{"type":"number"},"minimum":{"type":"number"},"exclusiveMinimum":{"type":"number"},"maxLength":{"$ref":"#/definitions/nonNegativeInteger"},"minLength":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"pattern":{"type":"string","format":"regex"},"additionalItems":{"$ref":"#"},"items":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/schemaArray"}],"default":true},"maxItems":{"$ref":"#/definitions/nonNegativeInteger"},"minItems":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"uniqueItems":{"type":"boolean","default":false},"contains":{"$ref":"#"},"maxProperties":{"$ref":"#/definitions/nonNegativeInteger"},"minProperties":{"$ref":"#/definitions/nonNegativeIntegerDefault0"},"required":{"$ref":"#/definitions/stringArray"},"additionalProperties":{"$ref":"#"},"definitions":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"properties":{"type":"object","additionalProperties":{"$ref":"#"},"default":{}},"patternProperties":{"type":"object","additionalProperties":{"$ref":"#"},"propertyNames":{"format":"regex"},"default":{}},"dependencies":{"type":"object","additionalProperties":{"anyOf":[{"$ref":"#"},{"$ref":"#/definitions/stringArray"}]}},"propertyNames":{"$ref":"#"},"const":true,"enum":{"type":"array","items":true,"minItems":1,"uniqueItems":true},"type":{"anyOf":[{"$ref":"#/definitions/simpleTypes"},{"type":"array","items":{"$ref":"#/definitions/simpleTypes"},"minItems":1,"uniqueItems":true}]},"format":{"type":"string"},"contentMediaType":{"type":"string"},"contentEncoding":{"type":"string"},"if":{"$ref":"#"},"then":{"$ref":"#"},"else":{"$ref":"#"},"allOf":{"$ref":"#/definitions/schemaArray"},"anyOf":{"$ref":"#/definitions/schemaArray"},"oneOf":{"$ref":"#/definitions/schemaArray"},"not":{"$ref":"#"}},"default":true}`, | |
60 | }, | |
61 | } | |
62 | } | |
63 | ||
64 | func (dc draftConfigs) GetMetaSchema(url string) string { | |
65 | for _, config := range dc { | |
66 | if config.MetaSchemaURL == url { | |
67 | return config.MetaSchema | |
68 | } | |
69 | } | |
70 | return "" | |
71 | } | |
72 | func (dc draftConfigs) GetDraftVersion(url string) *Draft { | |
73 | for _, config := range dc { | |
74 | if config.MetaSchemaURL == url { | |
75 | return &config.Version | |
76 | } | |
77 | } | |
78 | return nil | |
79 | } | |
80 | func (dc draftConfigs) GetSchemaURL(draft Draft) string { | |
81 | for _, config := range dc { | |
82 | if config.Version == draft { | |
83 | return config.MetaSchemaURL | |
84 | } | |
85 | } | |
86 | return "" | |
87 | } | |
88 | ||
89 | func parseSchemaURL(documentNode interface{}) (string, *Draft, error) { | |
90 | ||
91 | if isKind(documentNode, reflect.Bool) { | |
92 | return "", nil, nil | |
93 | } | |
94 | ||
95 | if !isKind(documentNode, reflect.Map) { | |
96 | return "", nil, errors.New("schema is invalid") | |
97 | } | |
98 | ||
99 | m := documentNode.(map[string]interface{}) | |
100 | ||
101 | if existsMapKey(m, KEY_SCHEMA) { | |
102 | if !isKind(m[KEY_SCHEMA], reflect.String) { | |
103 | return "", nil, errors.New(formatErrorDescription( | |
104 | Locale.MustBeOfType(), | |
105 | ErrorDetails{ | |
106 | "key": KEY_SCHEMA, | |
107 | "type": TYPE_STRING, | |
108 | }, | |
109 | )) | |
110 | } | |
111 | ||
112 | schemaReference, err := gojsonreference.NewJsonReference(m[KEY_SCHEMA].(string)) | |
113 | ||
114 | if err != nil { | |
115 | return "", nil, err | |
116 | } | |
117 | ||
118 | schema := schemaReference.String() | |
119 | ||
120 | return schema, drafts.GetDraftVersion(schema), nil | |
121 | } | |
122 | ||
123 | return "", nil, nil | |
124 | } |
5 | 5 | "text/template" |
6 | 6 | ) |
7 | 7 | |
8 | var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"),sync.RWMutex{}} | |
8 | var errorTemplates = errorTemplate{template.New("errors-new"), sync.RWMutex{}} | |
9 | 9 | |
10 | 10 | // template.Template is not thread-safe for writing, so some locking is done |
11 | 11 | // sync.RWMutex is used for efficiently locking when new templates are created |
15 | 15 | } |
16 | 16 | |
17 | 17 | type ( |
18 | // RequiredError. ErrorDetails: property string | |
18 | ||
19 | // FalseError. ErrorDetails: - | |
20 | FalseError struct { | |
21 | ResultErrorFields | |
22 | } | |
23 | ||
24 | // RequiredError indicates that a required field is missing | |
25 | // ErrorDetails: property string | |
19 | 26 | RequiredError struct { |
20 | 27 | ResultErrorFields |
21 | 28 | } |
22 | 29 | |
23 | // InvalidTypeError. ErrorDetails: expected, given | |
30 | // InvalidTypeError indicates that a field has the incorrect type | |
31 | // ErrorDetails: expected, given | |
24 | 32 | InvalidTypeError struct { |
25 | 33 | ResultErrorFields |
26 | 34 | } |
27 | 35 | |
28 | // NumberAnyOfError. ErrorDetails: - | |
36 | // NumberAnyOfError is produced in case of a failing "anyOf" validation | |
37 | // ErrorDetails: - | |
29 | 38 | NumberAnyOfError struct { |
30 | 39 | ResultErrorFields |
31 | 40 | } |
32 | 41 | |
33 | // NumberOneOfError. ErrorDetails: - | |
42 | // NumberOneOfError is produced in case of a failing "oneOf" validation | |
43 | // ErrorDetails: - | |
34 | 44 | NumberOneOfError struct { |
35 | 45 | ResultErrorFields |
36 | 46 | } |
37 | 47 | |
38 | // NumberAllOfError. ErrorDetails: - | |
48 | // NumberAllOfError is produced in case of a failing "allOf" validation | |
49 | // ErrorDetails: - | |
39 | 50 | NumberAllOfError struct { |
40 | 51 | ResultErrorFields |
41 | 52 | } |
42 | 53 | |
43 | // NumberNotError. ErrorDetails: - | |
54 | // NumberNotError is produced if a "not" validation failed | |
55 | // ErrorDetails: - | |
44 | 56 | NumberNotError struct { |
45 | 57 | ResultErrorFields |
46 | 58 | } |
47 | 59 | |
48 | // MissingDependencyError. ErrorDetails: dependency | |
60 | // MissingDependencyError is produced in case of a "missing dependency" problem | |
61 | // ErrorDetails: dependency | |
49 | 62 | MissingDependencyError struct { |
50 | 63 | ResultErrorFields |
51 | 64 | } |
52 | 65 | |
53 | // InternalError. ErrorDetails: error | |
66 | // InternalError indicates an internal error | |
67 | // ErrorDetails: error | |
54 | 68 | InternalError struct { |
55 | 69 | ResultErrorFields |
56 | 70 | } |
57 | 71 | |
58 | // EnumError. ErrorDetails: allowed | |
72 | // ConstError indicates a const error | |
73 | // ErrorDetails: allowed | |
74 | ConstError struct { | |
75 | ResultErrorFields | |
76 | } | |
77 | ||
78 | // EnumError indicates an enum error | |
79 | // ErrorDetails: allowed | |
59 | 80 | EnumError struct { |
60 | 81 | ResultErrorFields |
61 | 82 | } |
62 | 83 | |
63 | // ArrayNoAdditionalItemsError. ErrorDetails: - | |
84 | // ArrayNoAdditionalItemsError is produced if additional items were found, but not allowed | |
85 | // ErrorDetails: - | |
64 | 86 | ArrayNoAdditionalItemsError struct { |
65 | 87 | ResultErrorFields |
66 | 88 | } |
67 | 89 | |
68 | // ArrayMinItemsError. ErrorDetails: min | |
90 | // ArrayMinItemsError is produced if an array contains less items than the allowed minimum | |
91 | // ErrorDetails: min | |
69 | 92 | ArrayMinItemsError struct { |
70 | 93 | ResultErrorFields |
71 | 94 | } |
72 | 95 | |
73 | // ArrayMaxItemsError. ErrorDetails: max | |
96 | // ArrayMaxItemsError is produced if an array contains more items than the allowed maximum | |
97 | // ErrorDetails: max | |
74 | 98 | ArrayMaxItemsError struct { |
75 | 99 | ResultErrorFields |
76 | 100 | } |
77 | 101 | |
78 | // ItemsMustBeUniqueError. ErrorDetails: type | |
102 | // ItemsMustBeUniqueError is produced if an array requires unique items, but contains non-unique items | |
103 | // ErrorDetails: type, i, j | |
79 | 104 | ItemsMustBeUniqueError struct { |
80 | 105 | ResultErrorFields |
81 | 106 | } |
82 | 107 | |
83 | // ArrayMinPropertiesError. ErrorDetails: min | |
108 | // ArrayContainsError is produced if an array contains invalid items | |
109 | // ErrorDetails: | |
110 | ArrayContainsError struct { | |
111 | ResultErrorFields | |
112 | } | |
113 | ||
114 | // ArrayMinPropertiesError is produced if an object contains less properties than the allowed minimum | |
115 | // ErrorDetails: min | |
84 | 116 | ArrayMinPropertiesError struct { |
85 | 117 | ResultErrorFields |
86 | 118 | } |
87 | 119 | |
88 | // ArrayMaxPropertiesError. ErrorDetails: max | |
120 | // ArrayMaxPropertiesError is produced if an object contains more properties than the allowed maximum | |
121 | // ErrorDetails: max | |
89 | 122 | ArrayMaxPropertiesError struct { |
90 | 123 | ResultErrorFields |
91 | 124 | } |
92 | 125 | |
93 | // AdditionalPropertyNotAllowedError. ErrorDetails: property | |
126 | // AdditionalPropertyNotAllowedError is produced if an object has additional properties, but not allowed | |
127 | // ErrorDetails: property | |
94 | 128 | AdditionalPropertyNotAllowedError struct { |
95 | 129 | ResultErrorFields |
96 | 130 | } |
97 | 131 | |
98 | // InvalidPropertyPatternError. ErrorDetails: property, pattern | |
132 | // InvalidPropertyPatternError is produced if an pattern was found | |
133 | // ErrorDetails: property, pattern | |
99 | 134 | InvalidPropertyPatternError struct { |
100 | 135 | ResultErrorFields |
101 | 136 | } |
102 | 137 | |
103 | // StringLengthGTEError. ErrorDetails: min | |
138 | // InvalidPropertyNameError is produced if an invalid-named property was found | |
139 | // ErrorDetails: property | |
140 | InvalidPropertyNameError struct { | |
141 | ResultErrorFields | |
142 | } | |
143 | ||
144 | // StringLengthGTEError is produced if a string is shorter than the minimum required length | |
145 | // ErrorDetails: min | |
104 | 146 | StringLengthGTEError struct { |
105 | 147 | ResultErrorFields |
106 | 148 | } |
107 | 149 | |
108 | // StringLengthLTEError. ErrorDetails: max | |
150 | // StringLengthLTEError is produced if a string is longer than the maximum allowed length | |
151 | // ErrorDetails: max | |
109 | 152 | StringLengthLTEError struct { |
110 | 153 | ResultErrorFields |
111 | 154 | } |
112 | 155 | |
113 | // DoesNotMatchPatternError. ErrorDetails: pattern | |
156 | // DoesNotMatchPatternError is produced if a string does not match the defined pattern | |
157 | // ErrorDetails: pattern | |
114 | 158 | DoesNotMatchPatternError struct { |
115 | 159 | ResultErrorFields |
116 | 160 | } |
117 | 161 | |
118 | // DoesNotMatchFormatError. ErrorDetails: format | |
162 | // DoesNotMatchFormatError is produced if a string does not match the defined format | |
163 | // ErrorDetails: format | |
119 | 164 | DoesNotMatchFormatError struct { |
120 | 165 | ResultErrorFields |
121 | 166 | } |
122 | 167 | |
123 | // MultipleOfError. ErrorDetails: multiple | |
168 | // MultipleOfError is produced if a number is not a multiple of the defined multipleOf | |
169 | // ErrorDetails: multiple | |
124 | 170 | MultipleOfError struct { |
125 | 171 | ResultErrorFields |
126 | 172 | } |
127 | 173 | |
128 | // NumberGTEError. ErrorDetails: min | |
174 | // NumberGTEError is produced if a number is lower than the allowed minimum | |
175 | // ErrorDetails: min | |
129 | 176 | NumberGTEError struct { |
130 | 177 | ResultErrorFields |
131 | 178 | } |
132 | 179 | |
133 | // NumberGTError. ErrorDetails: min | |
180 | // NumberGTError is produced if a number is lower than, or equal to the specified minimum, and exclusiveMinimum is set | |
181 | // ErrorDetails: min | |
134 | 182 | NumberGTError struct { |
135 | 183 | ResultErrorFields |
136 | 184 | } |
137 | 185 | |
138 | // NumberLTEError. ErrorDetails: max | |
186 | // NumberLTEError is produced if a number is higher than the allowed maximum | |
187 | // ErrorDetails: max | |
139 | 188 | NumberLTEError struct { |
140 | 189 | ResultErrorFields |
141 | 190 | } |
142 | 191 | |
143 | // NumberLTError. ErrorDetails: max | |
192 | // NumberLTError is produced if a number is higher than, or equal to the specified maximum, and exclusiveMaximum is set | |
193 | // ErrorDetails: max | |
144 | 194 | NumberLTError struct { |
145 | 195 | ResultErrorFields |
146 | 196 | } |
197 | ||
198 | // ConditionThenError is produced if a condition's "then" validation is invalid | |
199 | // ErrorDetails: - | |
200 | ConditionThenError struct { | |
201 | ResultErrorFields | |
202 | } | |
203 | ||
204 | // ConditionElseError is produced if a condition's "else" condition is invalid | |
205 | // ErrorDetails: - | |
206 | ConditionElseError struct { | |
207 | ResultErrorFields | |
208 | } | |
147 | 209 | ) |
148 | 210 | |
149 | 211 | // newError takes a ResultError type and sets the type, context, description, details, value, and field |
150 | func newError(err ResultError, context *jsonContext, value interface{}, locale locale, details ErrorDetails) { | |
212 | func newError(err ResultError, context *JsonContext, value interface{}, locale locale, details ErrorDetails) { | |
151 | 213 | var t string |
152 | 214 | var d string |
153 | 215 | switch err.(type) { |
216 | case *FalseError: | |
217 | t = "false" | |
218 | d = locale.False() | |
154 | 219 | case *RequiredError: |
155 | 220 | t = "required" |
156 | 221 | d = locale.Required() |
175 | 240 | case *InternalError: |
176 | 241 | t = "internal" |
177 | 242 | d = locale.Internal() |
243 | case *ConstError: | |
244 | t = "const" | |
245 | d = locale.Const() | |
178 | 246 | case *EnumError: |
179 | 247 | t = "enum" |
180 | 248 | d = locale.Enum() |
190 | 258 | case *ItemsMustBeUniqueError: |
191 | 259 | t = "unique" |
192 | 260 | d = locale.Unique() |
261 | case *ArrayContainsError: | |
262 | t = "contains" | |
263 | d = locale.ArrayContains() | |
193 | 264 | case *ArrayMinPropertiesError: |
194 | 265 | t = "array_min_properties" |
195 | 266 | d = locale.ArrayMinProperties() |
202 | 273 | case *InvalidPropertyPatternError: |
203 | 274 | t = "invalid_property_pattern" |
204 | 275 | d = locale.InvalidPropertyPattern() |
276 | case *InvalidPropertyNameError: | |
277 | t = "invalid_property_name" | |
278 | d = locale.InvalidPropertyName() | |
205 | 279 | case *StringLengthGTEError: |
206 | 280 | t = "string_gte" |
207 | 281 | d = locale.StringGTE() |
229 | 303 | case *NumberLTError: |
230 | 304 | t = "number_lt" |
231 | 305 | d = locale.NumberLT() |
306 | case *ConditionThenError: | |
307 | t = "condition_then" | |
308 | d = locale.ConditionThen() | |
309 | case *ConditionElseError: | |
310 | t = "condition_else" | |
311 | d = locale.ConditionElse() | |
232 | 312 | } |
233 | 313 | |
234 | 314 | err.SetType(t) |
235 | 315 | err.SetContext(context) |
236 | 316 | err.SetValue(value) |
237 | 317 | err.SetDetails(details) |
318 | err.SetDescriptionFormat(d) | |
238 | 319 | details["field"] = err.Field() |
239 | err.SetDescription(formatErrorDescription(d, details)) | |
320 | ||
321 | if _, exists := details["context"]; !exists && context != nil { | |
322 | details["context"] = context.String() | |
323 | } | |
324 | ||
325 | err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details)) | |
240 | 326 | } |
241 | 327 | |
242 | 328 | // formatErrorDescription takes a string in the default text/template |
256 | 342 | errorTemplates.Lock() |
257 | 343 | tpl = errorTemplates.New(s) |
258 | 344 | |
345 | if ErrorTemplateFuncs != nil { | |
346 | tpl.Funcs(ErrorTemplateFuncs) | |
347 | } | |
348 | ||
259 | 349 | tpl, err = tpl.Parse(s) |
260 | 350 | errorTemplates.Unlock() |
261 | 351 |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "net" |
4 | "net/mail" | |
4 | 5 | "net/url" |
5 | "reflect" | |
6 | 6 | "regexp" |
7 | 7 | "strings" |
8 | "sync" | |
8 | 9 | "time" |
9 | 10 | ) |
10 | 11 | |
11 | 12 | type ( |
12 | 13 | // FormatChecker is the interface all formatters added to FormatCheckerChain must implement |
13 | 14 | FormatChecker interface { |
14 | IsFormat(input string) bool | |
15 | // IsFormat checks if input has the correct format and type | |
16 | IsFormat(input interface{}) bool | |
15 | 17 | } |
16 | 18 | |
17 | 19 | // FormatCheckerChain holds the formatters |
19 | 21 | formatters map[string]FormatChecker |
20 | 22 | } |
21 | 23 | |
22 | // EmailFormatter verifies email address formats | |
24 | // EmailFormatChecker verifies email address formats | |
23 | 25 | EmailFormatChecker struct{} |
24 | 26 | |
25 | // IPV4FormatChecker verifies IP addresses in the ipv4 format | |
27 | // IPV4FormatChecker verifies IP addresses in the IPv4 format | |
26 | 28 | IPV4FormatChecker struct{} |
27 | 29 | |
28 | // IPV6FormatChecker verifies IP addresses in the ipv6 format | |
30 | // IPV6FormatChecker verifies IP addresses in the IPv6 format | |
29 | 31 | IPV6FormatChecker struct{} |
30 | 32 | |
31 | 33 | // DateTimeFormatChecker verifies date/time formats per RFC3339 5.6 |
51 | 53 | // http://tools.ietf.org/html/rfc3339#section-5.6 |
52 | 54 | DateTimeFormatChecker struct{} |
53 | 55 | |
54 | // URIFormatCheckers validates a URI with a valid Scheme per RFC3986 | |
56 | // DateFormatChecker verifies date formats | |
57 | // | |
58 | // Valid format: | |
59 | // Full Date: YYYY-MM-DD | |
60 | // | |
61 | // Where | |
62 | // YYYY = 4DIGIT year | |
63 | // MM = 2DIGIT month ; 01-12 | |
64 | // DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year | |
65 | DateFormatChecker struct{} | |
66 | ||
67 | // TimeFormatChecker verifies time formats | |
68 | // | |
69 | // Valid formats: | |
70 | // Partial Time: HH:MM:SS | |
71 | // Full Time: HH:MM:SSZ-07:00 | |
72 | // | |
73 | // Where | |
74 | // HH = 2DIGIT hour ; 00-23 | |
75 | // MM = 2DIGIT ; 00-59 | |
76 | // SS = 2DIGIT ; 00-58, 00-60 based on leap second rules | |
77 | // T = Literal | |
78 | // Z = Literal | |
79 | TimeFormatChecker struct{} | |
80 | ||
81 | // URIFormatChecker validates a URI with a valid Scheme per RFC3986 | |
55 | 82 | URIFormatChecker struct{} |
83 | ||
84 | // URIReferenceFormatChecker validates a URI or relative-reference per RFC3986 | |
85 | URIReferenceFormatChecker struct{} | |
86 | ||
87 | // URITemplateFormatChecker validates a URI template per RFC6570 | |
88 | URITemplateFormatChecker struct{} | |
56 | 89 | |
57 | 90 | // HostnameFormatChecker validates a hostname is in the correct format |
58 | 91 | HostnameFormatChecker struct{} |
62 | 95 | |
63 | 96 | // RegexFormatChecker validates a regex is in the correct format |
64 | 97 | RegexFormatChecker struct{} |
98 | ||
99 | // JSONPointerFormatChecker validates a JSON Pointer per RFC6901 | |
100 | JSONPointerFormatChecker struct{} | |
101 | ||
102 | // RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format | |
103 | RelativeJSONPointerFormatChecker struct{} | |
65 | 104 | ) |
66 | 105 | |
67 | 106 | var ( |
68 | // Formatters holds the valid formatters, and is a public variable | |
107 | // FormatCheckers holds the valid formatters, and is a public variable | |
69 | 108 | // so library users can add custom formatters |
70 | 109 | FormatCheckers = FormatCheckerChain{ |
71 | 110 | formatters: map[string]FormatChecker{ |
72 | "date-time": DateTimeFormatChecker{}, | |
73 | "hostname": HostnameFormatChecker{}, | |
74 | "email": EmailFormatChecker{}, | |
75 | "ipv4": IPV4FormatChecker{}, | |
76 | "ipv6": IPV6FormatChecker{}, | |
77 | "uri": URIFormatChecker{}, | |
78 | "uuid": UUIDFormatChecker{}, | |
79 | "regex": RegexFormatChecker{}, | |
111 | "date": DateFormatChecker{}, | |
112 | "time": TimeFormatChecker{}, | |
113 | "date-time": DateTimeFormatChecker{}, | |
114 | "hostname": HostnameFormatChecker{}, | |
115 | "email": EmailFormatChecker{}, | |
116 | "idn-email": EmailFormatChecker{}, | |
117 | "ipv4": IPV4FormatChecker{}, | |
118 | "ipv6": IPV6FormatChecker{}, | |
119 | "uri": URIFormatChecker{}, | |
120 | "uri-reference": URIReferenceFormatChecker{}, | |
121 | "iri": URIFormatChecker{}, | |
122 | "iri-reference": URIReferenceFormatChecker{}, | |
123 | "uri-template": URITemplateFormatChecker{}, | |
124 | "uuid": UUIDFormatChecker{}, | |
125 | "regex": RegexFormatChecker{}, | |
126 | "json-pointer": JSONPointerFormatChecker{}, | |
127 | "relative-json-pointer": RelativeJSONPointerFormatChecker{}, | |
80 | 128 | }, |
81 | 129 | } |
82 | ||
83 | // Regex credit: https://github.com/asaskevich/govalidator | |
84 | rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$") | |
85 | 130 | |
86 | 131 | // Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname |
87 | 132 | rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`) |
88 | 133 | |
134 | // Use a regex to make sure curly brackets are balanced properly after validating it as a AURI | |
135 | rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$") | |
136 | ||
89 | 137 | rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$") |
138 | ||
139 | rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$") | |
140 | ||
141 | rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$") | |
142 | ||
143 | lock = new(sync.RWMutex) | |
90 | 144 | ) |
91 | 145 | |
92 | 146 | // Add adds a FormatChecker to the FormatCheckerChain |
93 | 147 | // The name used will be the value used for the format key in your json schema |
94 | 148 | func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain { |
149 | lock.Lock() | |
95 | 150 | c.formatters[name] = f |
151 | lock.Unlock() | |
96 | 152 | |
97 | 153 | return c |
98 | 154 | } |
99 | 155 | |
100 | 156 | // Remove deletes a FormatChecker from the FormatCheckerChain (if it exists) |
101 | 157 | func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain { |
158 | lock.Lock() | |
102 | 159 | delete(c.formatters, name) |
160 | lock.Unlock() | |
103 | 161 | |
104 | 162 | return c |
105 | 163 | } |
106 | 164 | |
107 | 165 | // Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name |
108 | 166 | func (c *FormatCheckerChain) Has(name string) bool { |
167 | lock.RLock() | |
109 | 168 | _, ok := c.formatters[name] |
169 | lock.RUnlock() | |
110 | 170 | |
111 | 171 | return ok |
112 | 172 | } |
114 | 174 | // IsFormat will check an input against a FormatChecker with the given name |
115 | 175 | // to see if it is the correct format |
116 | 176 | func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool { |
177 | lock.RLock() | |
117 | 178 | f, ok := c.formatters[name] |
118 | ||
119 | if !ok { | |
120 | return false | |
121 | } | |
122 | ||
123 | if !isKind(input, reflect.String) { | |
124 | return false | |
125 | } | |
126 | ||
127 | inputString := input.(string) | |
128 | ||
129 | return f.IsFormat(inputString) | |
130 | } | |
131 | ||
132 | func (f EmailFormatChecker) IsFormat(input string) bool { | |
133 | return rxEmail.MatchString(input) | |
134 | } | |
135 | ||
136 | // Credit: https://github.com/asaskevich/govalidator | |
137 | func (f IPV4FormatChecker) IsFormat(input string) bool { | |
138 | ip := net.ParseIP(input) | |
139 | return ip != nil && strings.Contains(input, ".") | |
140 | } | |
141 | ||
142 | // Credit: https://github.com/asaskevich/govalidator | |
143 | func (f IPV6FormatChecker) IsFormat(input string) bool { | |
144 | ip := net.ParseIP(input) | |
145 | return ip != nil && strings.Contains(input, ":") | |
146 | } | |
147 | ||
148 | func (f DateTimeFormatChecker) IsFormat(input string) bool { | |
179 | lock.RUnlock() | |
180 | ||
181 | // If a format is unrecognized it should always pass validation | |
182 | if !ok { | |
183 | return true | |
184 | } | |
185 | ||
186 | return f.IsFormat(input) | |
187 | } | |
188 | ||
189 | // IsFormat checks if input is a correctly formatted e-mail address | |
190 | func (f EmailFormatChecker) IsFormat(input interface{}) bool { | |
191 | asString, ok := input.(string) | |
192 | if !ok { | |
193 | return false | |
194 | } | |
195 | ||
196 | _, err := mail.ParseAddress(asString) | |
197 | return err == nil | |
198 | } | |
199 | ||
200 | // IsFormat checks if input is a correctly formatted IPv4-address | |
201 | func (f IPV4FormatChecker) IsFormat(input interface{}) bool { | |
202 | asString, ok := input.(string) | |
203 | if !ok { | |
204 | return false | |
205 | } | |
206 | ||
207 | // Credit: https://github.com/asaskevich/govalidator | |
208 | ip := net.ParseIP(asString) | |
209 | return ip != nil && strings.Contains(asString, ".") | |
210 | } | |
211 | ||
212 | // IsFormat checks if input is a correctly formatted IPv6=address | |
213 | func (f IPV6FormatChecker) IsFormat(input interface{}) bool { | |
214 | asString, ok := input.(string) | |
215 | if !ok { | |
216 | return false | |
217 | } | |
218 | ||
219 | // Credit: https://github.com/asaskevich/govalidator | |
220 | ip := net.ParseIP(asString) | |
221 | return ip != nil && strings.Contains(asString, ":") | |
222 | } | |
223 | ||
224 | // IsFormat checks if input is a correctly formatted date/time per RFC3339 5.6 | |
225 | func (f DateTimeFormatChecker) IsFormat(input interface{}) bool { | |
226 | asString, ok := input.(string) | |
227 | if !ok { | |
228 | return false | |
229 | } | |
230 | ||
149 | 231 | formats := []string{ |
150 | 232 | "15:04:05", |
151 | 233 | "15:04:05Z07:00", |
155 | 237 | } |
156 | 238 | |
157 | 239 | for _, format := range formats { |
158 | if _, err := time.Parse(format, input); err == nil { | |
240 | if _, err := time.Parse(format, asString); err == nil { | |
159 | 241 | return true |
160 | 242 | } |
161 | 243 | } |
163 | 245 | return false |
164 | 246 | } |
165 | 247 | |
166 | func (f URIFormatChecker) IsFormat(input string) bool { | |
167 | u, err := url.Parse(input) | |
248 | // IsFormat checks if input is a correctly formatted date (YYYY-MM-DD) | |
249 | func (f DateFormatChecker) IsFormat(input interface{}) bool { | |
250 | asString, ok := input.(string) | |
251 | if !ok { | |
252 | return false | |
253 | } | |
254 | _, err := time.Parse("2006-01-02", asString) | |
255 | return err == nil | |
256 | } | |
257 | ||
258 | // IsFormat checks if input correctly formatted time (HH:MM:SS or HH:MM:SSZ-07:00) | |
259 | func (f TimeFormatChecker) IsFormat(input interface{}) bool { | |
260 | asString, ok := input.(string) | |
261 | if !ok { | |
262 | return false | |
263 | } | |
264 | ||
265 | if _, err := time.Parse("15:04:05Z07:00", asString); err == nil { | |
266 | return true | |
267 | } | |
268 | ||
269 | _, err := time.Parse("15:04:05", asString) | |
270 | return err == nil | |
271 | } | |
272 | ||
273 | // IsFormat checks if input is correctly formatted URI with a valid Scheme per RFC3986 | |
274 | func (f URIFormatChecker) IsFormat(input interface{}) bool { | |
275 | asString, ok := input.(string) | |
276 | if !ok { | |
277 | return false | |
278 | } | |
279 | ||
280 | u, err := url.Parse(asString) | |
281 | ||
168 | 282 | if err != nil || u.Scheme == "" { |
169 | 283 | return false |
170 | 284 | } |
171 | 285 | |
172 | return true | |
173 | } | |
174 | ||
175 | func (f HostnameFormatChecker) IsFormat(input string) bool { | |
176 | return rxHostname.MatchString(input) && len(input) < 256 | |
177 | } | |
178 | ||
179 | func (f UUIDFormatChecker) IsFormat(input string) bool { | |
180 | return rxUUID.MatchString(input) | |
181 | } | |
182 | ||
183 | // IsFormat implements FormatChecker interface. | |
184 | func (f RegexFormatChecker) IsFormat(input string) bool { | |
185 | if input == "" { | |
286 | return !strings.Contains(asString, `\`) | |
287 | } | |
288 | ||
289 | // IsFormat checks if input is a correctly formatted URI or relative-reference per RFC3986 | |
290 | func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool { | |
291 | asString, ok := input.(string) | |
292 | if !ok { | |
293 | return false | |
294 | } | |
295 | ||
296 | _, err := url.Parse(asString) | |
297 | return err == nil && !strings.Contains(asString, `\`) | |
298 | } | |
299 | ||
300 | // IsFormat checks if input is a correctly formatted URI template per RFC6570 | |
301 | func (f URITemplateFormatChecker) IsFormat(input interface{}) bool { | |
302 | asString, ok := input.(string) | |
303 | if !ok { | |
304 | return false | |
305 | } | |
306 | ||
307 | u, err := url.Parse(asString) | |
308 | if err != nil || strings.Contains(asString, `\`) { | |
309 | return false | |
310 | } | |
311 | ||
312 | return rxURITemplate.MatchString(u.Path) | |
313 | } | |
314 | ||
315 | // IsFormat checks if input is a correctly formatted hostname | |
316 | func (f HostnameFormatChecker) IsFormat(input interface{}) bool { | |
317 | asString, ok := input.(string) | |
318 | if !ok { | |
319 | return false | |
320 | } | |
321 | ||
322 | return rxHostname.MatchString(asString) && len(asString) < 256 | |
323 | } | |
324 | ||
325 | // IsFormat checks if input is a correctly formatted UUID | |
326 | func (f UUIDFormatChecker) IsFormat(input interface{}) bool { | |
327 | asString, ok := input.(string) | |
328 | if !ok { | |
329 | return false | |
330 | } | |
331 | ||
332 | return rxUUID.MatchString(asString) | |
333 | } | |
334 | ||
335 | // IsFormat checks if input is a correctly formatted regular expression | |
336 | func (f RegexFormatChecker) IsFormat(input interface{}) bool { | |
337 | asString, ok := input.(string) | |
338 | if !ok { | |
339 | return false | |
340 | } | |
341 | ||
342 | if asString == "" { | |
186 | 343 | return true |
187 | 344 | } |
188 | _, err := regexp.Compile(input) | |
189 | if err != nil { | |
190 | return false | |
191 | } | |
192 | return true | |
193 | } | |
345 | _, err := regexp.Compile(asString) | |
346 | return err == nil | |
347 | } | |
348 | ||
349 | // IsFormat checks if input is a correctly formatted JSON Pointer per RFC6901 | |
350 | func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool { | |
351 | asString, ok := input.(string) | |
352 | if !ok { | |
353 | return false | |
354 | } | |
355 | ||
356 | return rxJSONPointer.MatchString(asString) | |
357 | } | |
358 | ||
359 | // IsFormat checks if input is a correctly formatted relative JSON Pointer | |
360 | func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool { | |
361 | asString, ok := input.(string) | |
362 | if !ok { | |
363 | return false | |
364 | } | |
365 | ||
366 | return rxRelJSONPointer.MatchString(asString) | |
367 | } |
13 | 13 | assert.False(t, checker.IsFormat("not-a-uuid")) |
14 | 14 | assert.False(t, checker.IsFormat("g1234567-89ab-cdef-0123-456789abcdef")) |
15 | 15 | } |
16 | ||
17 | func TestURIReferenceFormatCheckerIsFormat(t *testing.T) { | |
18 | checker := URIReferenceFormatChecker{} | |
19 | ||
20 | assert.True(t, checker.IsFormat("relative")) | |
21 | assert.True(t, checker.IsFormat("https://dummyhost.com/dummy-path?dummy-qp-name=dummy-qp-value")) | |
22 | } |
6 | 6 | |
7 | 7 | - package: github.com/xeipuuv/gojsonreference |
8 | 8 | |
9 | - package: github.com/stretchr/testify/assert | |
10 | version: ^1.1.3 | |
11 | ||
9 | testImport: | |
10 | - package: github.com/stretchr/testify | |
11 | subpackages: | |
12 | - assert |
0 | module github.com/xeipuuv/gojsonschema | |
1 | ||
2 | require ( | |
3 | github.com/stretchr/testify v1.3.0 | |
4 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect | |
5 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 | |
6 | ) |
0 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | |
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
2 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |
3 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | |
4 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | |
5 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | |
6 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | |
7 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= | |
8 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= | |
9 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= | |
10 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= |
25 | 25 | |
26 | 26 | import "bytes" |
27 | 27 | |
28 | // jsonContext implements a persistent linked-list of strings | |
29 | type jsonContext struct { | |
28 | // JsonContext implements a persistent linked-list of strings | |
29 | type JsonContext struct { | |
30 | 30 | head string |
31 | tail *jsonContext | |
31 | tail *JsonContext | |
32 | 32 | } |
33 | 33 | |
34 | func newJsonContext(head string, tail *jsonContext) *jsonContext { | |
35 | return &jsonContext{head, tail} | |
34 | // NewJsonContext creates a new JsonContext | |
35 | func NewJsonContext(head string, tail *JsonContext) *JsonContext { | |
36 | return &JsonContext{head, tail} | |
36 | 37 | } |
37 | 38 | |
38 | 39 | // String displays the context in reverse. |
39 | 40 | // This plays well with the data structure's persistent nature with |
40 | 41 | // Cons and a json document's tree structure. |
41 | func (c *jsonContext) String(del ...string) string { | |
42 | func (c *JsonContext) String(del ...string) string { | |
42 | 43 | byteArr := make([]byte, 0, c.stringLen()) |
43 | 44 | buf := bytes.NewBuffer(byteArr) |
44 | 45 | c.writeStringToBuffer(buf, del) |
46 | 47 | return buf.String() |
47 | 48 | } |
48 | 49 | |
49 | func (c *jsonContext) stringLen() int { | |
50 | func (c *JsonContext) stringLen() int { | |
50 | 51 | length := 0 |
51 | 52 | if c.tail != nil { |
52 | 53 | length = c.tail.stringLen() + 1 // add 1 for "." |
56 | 57 | return length |
57 | 58 | } |
58 | 59 | |
59 | func (c *jsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) { | |
60 | func (c *JsonContext) writeStringToBuffer(buf *bytes.Buffer, del []string) { | |
60 | 61 | if c.tail != nil { |
61 | 62 | c.tail.writeStringToBuffer(buf, del) |
62 | 63 |
32 | 32 | "io" |
33 | 33 | "io/ioutil" |
34 | 34 | "net/http" |
35 | "net/url" | |
35 | 36 | "os" |
36 | 37 | "path/filepath" |
37 | 38 | "runtime" |
42 | 43 | |
43 | 44 | var osFS = osFileSystem(os.Open) |
44 | 45 | |
45 | // JSON loader interface | |
46 | ||
46 | // JSONLoader defines the JSON loader interface | |
47 | 47 | type JSONLoader interface { |
48 | 48 | JsonSource() interface{} |
49 | 49 | LoadJSON() (interface{}, error) |
51 | 51 | LoaderFactory() JSONLoaderFactory |
52 | 52 | } |
53 | 53 | |
54 | // JSONLoaderFactory defines the JSON loader factory interface | |
54 | 55 | type JSONLoaderFactory interface { |
56 | // New creates a new JSON loader for the given source | |
55 | 57 | New(source string) JSONLoader |
56 | 58 | } |
57 | 59 | |
60 | // DefaultJSONLoaderFactory is the default JSON loader factory | |
58 | 61 | type DefaultJSONLoaderFactory struct { |
59 | 62 | } |
60 | 63 | |
64 | // FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem | |
61 | 65 | type FileSystemJSONLoaderFactory struct { |
62 | 66 | fs http.FileSystem |
63 | 67 | } |
64 | 68 | |
69 | // New creates a new JSON loader for the given source | |
65 | 70 | func (d DefaultJSONLoaderFactory) New(source string) JSONLoader { |
66 | 71 | return &jsonReferenceLoader{ |
67 | 72 | fs: osFS, |
69 | 74 | } |
70 | 75 | } |
71 | 76 | |
77 | // New creates a new JSON loader for the given source | |
72 | 78 | func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader { |
73 | 79 | return &jsonReferenceLoader{ |
74 | 80 | fs: f.fs, |
79 | 85 | // osFileSystem is a functional wrapper for os.Open that implements http.FileSystem. |
80 | 86 | type osFileSystem func(string) (*os.File, error) |
81 | 87 | |
88 | // Opens a file with the given name | |
82 | 89 | func (o osFileSystem) Open(name string) (http.File, error) { |
83 | 90 | return o(name) |
84 | 91 | } |
106 | 113 | } |
107 | 114 | |
108 | 115 | // NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system. |
109 | func NewReferenceLoader(source string) *jsonReferenceLoader { | |
116 | func NewReferenceLoader(source string) JSONLoader { | |
110 | 117 | return &jsonReferenceLoader{ |
111 | 118 | fs: osFS, |
112 | 119 | source: source, |
114 | 121 | } |
115 | 122 | |
116 | 123 | // NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system. |
117 | func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader { | |
124 | func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) JSONLoader { | |
118 | 125 | return &jsonReferenceLoader{ |
119 | 126 | fs: fs, |
120 | 127 | source: source, |
130 | 137 | return nil, err |
131 | 138 | } |
132 | 139 | |
133 | refToUrl := reference | |
134 | refToUrl.GetUrl().Fragment = "" | |
140 | refToURL := reference | |
141 | refToURL.GetUrl().Fragment = "" | |
135 | 142 | |
136 | 143 | var document interface{} |
137 | 144 | |
138 | 145 | if reference.HasFileScheme { |
139 | 146 | |
140 | filename := strings.Replace(refToUrl.GetUrl().Path, "file://", "", -1) | |
147 | filename := strings.TrimPrefix(refToURL.String(), "file://") | |
148 | filename, err = url.QueryUnescape(filename) | |
149 | ||
150 | if err != nil { | |
151 | return nil, err | |
152 | } | |
153 | ||
141 | 154 | if runtime.GOOS == "windows" { |
142 | 155 | // on Windows, a file URL may have an extra leading slash, use slashes |
143 | 156 | // instead of backslashes, and have spaces escaped |
144 | if strings.HasPrefix(filename, "/") { | |
145 | filename = filename[1:] | |
146 | } | |
157 | filename = strings.TrimPrefix(filename, "/") | |
147 | 158 | filename = filepath.FromSlash(filename) |
148 | 159 | } |
149 | 160 | |
154 | 165 | |
155 | 166 | } else { |
156 | 167 | |
157 | document, err = l.loadFromHTTP(refToUrl.String()) | |
168 | document, err = l.loadFromHTTP(refToURL.String()) | |
158 | 169 | if err != nil { |
159 | 170 | return nil, err |
160 | 171 | } |
167 | 178 | |
168 | 179 | func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) { |
169 | 180 | |
181 | // returned cached versions for metaschemas for drafts 4, 6 and 7 | |
182 | // for performance and allow for easier offline use | |
183 | if metaSchema := drafts.GetMetaSchema(address); metaSchema != "" { | |
184 | return decodeJSONUsingNumber(strings.NewReader(metaSchema)) | |
185 | } | |
186 | ||
170 | 187 | resp, err := http.Get(address) |
171 | 188 | if err != nil { |
172 | 189 | return nil, err |
174 | 191 | |
175 | 192 | // must return HTTP Status 200 OK |
176 | 193 | if resp.StatusCode != http.StatusOK { |
177 | return nil, errors.New(formatErrorDescription(Locale.httpBadStatus(), ErrorDetails{"status": resp.Status})) | |
194 | return nil, errors.New(formatErrorDescription(Locale.HttpBadStatus(), ErrorDetails{"status": resp.Status})) | |
178 | 195 | } |
179 | 196 | |
180 | 197 | bodyBuff, err := ioutil.ReadAll(resp.Body) |
182 | 199 | return nil, err |
183 | 200 | } |
184 | 201 | |
185 | return decodeJsonUsingNumber(bytes.NewReader(bodyBuff)) | |
186 | ||
202 | return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) | |
187 | 203 | } |
188 | 204 | |
189 | 205 | func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) { |
198 | 214 | return nil, err |
199 | 215 | } |
200 | 216 | |
201 | return decodeJsonUsingNumber(bytes.NewReader(bodyBuff)) | |
217 | return decodeJSONUsingNumber(bytes.NewReader(bodyBuff)) | |
202 | 218 | |
203 | 219 | } |
204 | 220 | |
220 | 236 | return &DefaultJSONLoaderFactory{} |
221 | 237 | } |
222 | 238 | |
223 | func NewStringLoader(source string) *jsonStringLoader { | |
239 | // NewStringLoader creates a new JSONLoader, taking a string as source | |
240 | func NewStringLoader(source string) JSONLoader { | |
224 | 241 | return &jsonStringLoader{source: source} |
225 | 242 | } |
226 | 243 | |
227 | 244 | func (l *jsonStringLoader) LoadJSON() (interface{}, error) { |
228 | 245 | |
229 | return decodeJsonUsingNumber(strings.NewReader(l.JsonSource().(string))) | |
246 | return decodeJSONUsingNumber(strings.NewReader(l.JsonSource().(string))) | |
230 | 247 | |
231 | 248 | } |
232 | 249 | |
248 | 265 | return &DefaultJSONLoaderFactory{} |
249 | 266 | } |
250 | 267 | |
251 | func NewBytesLoader(source []byte) *jsonBytesLoader { | |
268 | // NewBytesLoader creates a new JSONLoader, taking a `[]byte` as source | |
269 | func NewBytesLoader(source []byte) JSONLoader { | |
252 | 270 | return &jsonBytesLoader{source: source} |
253 | 271 | } |
254 | 272 | |
255 | 273 | func (l *jsonBytesLoader) LoadJSON() (interface{}, error) { |
256 | return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte))) | |
274 | return decodeJSONUsingNumber(bytes.NewReader(l.JsonSource().([]byte))) | |
257 | 275 | } |
258 | 276 | |
259 | 277 | // JSON Go (types) loader |
275 | 293 | return &DefaultJSONLoaderFactory{} |
276 | 294 | } |
277 | 295 | |
278 | func NewGoLoader(source interface{}) *jsonGoLoader { | |
296 | // NewGoLoader creates a new JSONLoader from a given Go struct | |
297 | func NewGoLoader(source interface{}) JSONLoader { | |
279 | 298 | return &jsonGoLoader{source: source} |
280 | 299 | } |
281 | 300 | |
288 | 307 | return nil, err |
289 | 308 | } |
290 | 309 | |
291 | return decodeJsonUsingNumber(bytes.NewReader(jsonBytes)) | |
310 | return decodeJSONUsingNumber(bytes.NewReader(jsonBytes)) | |
292 | 311 | |
293 | 312 | } |
294 | 313 | |
296 | 315 | buf *bytes.Buffer |
297 | 316 | } |
298 | 317 | |
299 | func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) { | |
318 | // NewReaderLoader creates a new JSON loader using the provided io.Reader | |
319 | func NewReaderLoader(source io.Reader) (JSONLoader, io.Reader) { | |
300 | 320 | buf := &bytes.Buffer{} |
301 | 321 | return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf) |
302 | 322 | } |
303 | 323 | |
304 | func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) { | |
324 | // NewWriterLoader creates a new JSON loader using the provided io.Writer | |
325 | func NewWriterLoader(source io.Writer) (JSONLoader, io.Writer) { | |
305 | 326 | buf := &bytes.Buffer{} |
306 | 327 | return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf) |
307 | 328 | } |
311 | 332 | } |
312 | 333 | |
313 | 334 | func (l *jsonIOLoader) LoadJSON() (interface{}, error) { |
314 | return decodeJsonUsingNumber(l.buf) | |
335 | return decodeJSONUsingNumber(l.buf) | |
315 | 336 | } |
316 | 337 | |
317 | 338 | func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) { |
322 | 343 | return &DefaultJSONLoaderFactory{} |
323 | 344 | } |
324 | 345 | |
325 | func decodeJsonUsingNumber(r io.Reader) (interface{}, error) { | |
346 | // JSON raw loader | |
347 | // In case the JSON is already marshalled to interface{} use this loader | |
348 | // This is used for testing as otherwise there is no guarantee the JSON is marshalled | |
349 | // "properly" by using https://golang.org/pkg/encoding/json/#Decoder.UseNumber | |
350 | type jsonRawLoader struct { | |
351 | source interface{} | |
352 | } | |
353 | ||
354 | // NewRawLoader creates a new JSON raw loader for the given source | |
355 | func NewRawLoader(source interface{}) JSONLoader { | |
356 | return &jsonRawLoader{source: source} | |
357 | } | |
358 | func (l *jsonRawLoader) JsonSource() interface{} { | |
359 | return l.source | |
360 | } | |
361 | func (l *jsonRawLoader) LoadJSON() (interface{}, error) { | |
362 | return l.source, nil | |
363 | } | |
364 | func (l *jsonRawLoader) JsonReference() (gojsonreference.JsonReference, error) { | |
365 | return gojsonreference.NewJsonReference("#") | |
366 | } | |
367 | func (l *jsonRawLoader) LoaderFactory() JSONLoaderFactory { | |
368 | return &DefaultJSONLoaderFactory{} | |
369 | } | |
370 | ||
371 | func decodeJSONUsingNumber(r io.Reader) (interface{}, error) { | |
326 | 372 | |
327 | 373 | var document interface{} |
328 | 374 |
0 | Copyright (c) 2012 Julian Berman | |
1 | ||
2 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
3 | of this software and associated documentation files (the "Software"), to deal | |
4 | in the Software without restriction, including without limitation the rights | |
5 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
6 | copies of the Software, and to permit persons to whom the Software is | |
7 | furnished to do so, subject to the following conditions: | |
8 | ||
9 | The above copyright notice and this permission notice shall be included in | |
10 | all copies or substantial portions of the Software. | |
11 | ||
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
18 | THE SOFTWARE. |
0 | {"additionalItems":{"type":"integer"},"items":[{}]}⏎ |
0 | {"additionalItems":false,"items":{}}⏎ |
0 | {"additionalItems":false,"items":[{},{},{}]}⏎ |
0 | {"items":[{"type":"integer"}]}⏎ |
0 | {"bar":2,"foo":1,"quux":"boom"}⏎ |
0 | {"bar":2,"foo":1,"quux":true}⏎ |
0 | {"bar":2,"foo":1,"quux":12}⏎ |
0 | {"bar":2,"foo":1,"quux":true}⏎ |
0 | {"additionalProperties":false,"properties":{"bar":{},"foo":{}},"patternProperties": { "^v": {} }} |
0 | {"additionalProperties":{"type":"boolean"},"properties":{"bar":{},"foo":{}}}⏎ |
0 | {"properties":{"bar":{},"foo":{}}}⏎ |
0 | {"allOf":[{"properties":{"bar":{"type":"integer"}},"required":["bar"]},{"properties":{"foo":{"type":"string"}},"required":["foo"]}]}⏎ |
0 | {"allOf":[{"properties":{"foo":{"type":"string"}},"required":["foo"]},{"properties":{"baz":{"type":"null"}},"required":["baz"]}],"properties":{"bar":{"type":"integer"}},"required":["bar"]}⏎ |
0 | {"allOf":[{"maximum":30},{"minimum":20}]}⏎ |
0 | {"anyOf":[{"type":"integer"},{"minimum":2}]}⏎ |
0 | {"anyOf":[{"maxLength":2},{"minLength":4}],"type":"string"}⏎ |
0 | {"definitions":{"foo":{"type":"integer"}}}⏎ |
0 | {"definitions":{"foo":{"type":1}}}⏎ |
0 | {"$ref":"http://json-schema.org/draft-04/schema#"}⏎ |
0 | {"$ref":"http://json-schema.org/draft-04/schema#"}⏎ |
0 | {"dependencies":{"bar":["foo"]}}⏎ |
0 | {"dependencies":{"quux":["foo","bar"]}}⏎ |
0 | {"dependencies":{"bar":{"properties":{"bar":{"type":"integer"},"foo":{"type":"integer"}}}}}⏎ |
0 | "FE80:0000:0000:0000:0202:B3FF:FE1E:8329"⏎ |
0 | "1200:0000:AB00:1234:O000:2552:7777:1313"⏎ |
0 | {"type": "string", "format": "email"}⏎ |
0 | {"type": "string", "format": "invoice"}⏎ |
0 | {"type": "string", "format": "date-time"}⏎ |
0 | {"type": "string", "format": "hostname"}⏎ |
0 | {"items":[{"type":"integer"},{"type":"string"}]}⏎ |
0 | {"exclusiveMaximum":true,"maximum":3}⏎ |
0 | {"exclusiveMinimum":true,"minimum":1.1}⏎ |
0 | {"not":{"properties":{"foo":{"type":"string"}},"type":"object"}}⏎ |
0 | {"oneOf":[{"type":"integer"},{"minimum":2}]}⏎ |
0 | {"oneOf":[{"minLength":2},{"maxLength":4}],"type":"string"}⏎ |
0 | {"foo":"bar","foooooo":"baz"}⏎ |
0 | {"patternProperties":{"f.*o":{"type":"integer"}}}⏎ |
0 | {"patternProperties":{"a*":{"type":"integer"},"aaa*":{"maximum":20}}}⏎ |
0 | {"patternProperties":{"X_":{"type":"string"},"[0-9]{2,}":{"type":"boolean"}}}⏎ |
0 | { | |
1 | "$schema": "http://json-schema.org/draft-04/schema#", | |
2 | "type": "object", | |
3 | "patternProperties": { | |
4 | "^[a-zA-Z0-9]{1,10}$": { "type": "string" } | |
5 | }, | |
6 | "additionalProperties": false | |
7 | }⏎ |
0 | { | |
1 | "$schema": "http://json-schema.org/draft-04/schema#", | |
2 | "type": "object", | |
3 | "properties": { | |
4 | "dictionary": { | |
5 | "type": "object", | |
6 | "patternProperties": { | |
7 | "^[a-zA-Z0-9]{1,10}$": { "type": "string" } | |
8 | }, | |
9 | "additionalProperties": false | |
10 | } | |
11 | }, | |
12 | "additionalProperties": false | |
13 | }⏎ |
0 | {"properties":{"bar":{"type":"string"},"foo":{"type":"integer"}}}⏎ |
0 | {"additionalProperties":{"type":"integer"},"patternProperties":{"f.o":{"minItems":2}},"properties":{"bar":{"type":"array"},"foo":{"maxItems":3,"type":"array"}}}⏎ |
0 | {"additionalProperties":false,"properties":{"foo":{"$ref":"#"}}}⏎ |
0 | {"properties":{"bar":{"$ref":"#/properties/foo"},"foo":{"type":"integer"}}}⏎ |
0 | {"items":[{"type":"integer"},{"$ref":"#/items/0"}]}⏎ |
0 | {"percent%field":{"type":"integer"},"properties":{"percent":{"$ref":"#/percent%25field"},"slash":{"$ref":"#/slash~1field"},"tilda":{"$ref":"#/tilda~0field"}},"slash/field":{"type":"integer"},"tilda~field":{"type":"integer"}}⏎ |
0 | {"$ref":"#/definitions/c","definitions":{"a":{"type":"integer"},"b":{"$ref":"#/definitions/a"},"c":{"$ref":"#/definitions/b"}}}⏎ |
0 | {"$ref":"http://json-schema.org/draft-04/schema#"}⏎ |
0 | {"$ref":"http://localhost:1234/integer.json"}⏎ |
0 | {"$ref":"http://localhost:1234/subSchemas.json#/integer"}⏎ |
0 | {"$ref":"http://localhost:1234/subSchemas.json#/refToInteger"}⏎ |
0 | {"id":"http://localhost:1234","items":{"id":"folder/","items":{"$ref":"folderInteger.json"}}}⏎ |
0 | {"properties":{"bar":{},"foo":{}},"required":["foo"]}⏎ |
0 | [{"foo":{"bar":{"baz":true}}},{"foo":{"bar":{"baz":false}}}]⏎ |
0 | [{"foo":{"bar":{"baz":true}}},{"foo":{"bar":{"baz":true}}}]⏎ |
0 | // Copyright 2017 johandorland ( https://github.com/johandorland ) | |
1 | // | |
2 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | // you may not use this file except in compliance with the License. | |
4 | // You may obtain a copy of the License at | |
5 | // | |
6 | // http://www.apache.org/licenses/LICENSE-2.0 | |
7 | // | |
8 | // Unless required by applicable law or agreed to in writing, software | |
9 | // distributed under the License is distributed on an "AS IS" BASIS, | |
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | // See the License for the specific language governing permissions and | |
12 | // limitations under the License. | |
13 | ||
14 | package gojsonschema | |
15 | ||
16 | import ( | |
17 | "encoding/json" | |
18 | "fmt" | |
19 | "io/ioutil" | |
20 | "net/http" | |
21 | "os" | |
22 | "path/filepath" | |
23 | "regexp" | |
24 | "strings" | |
25 | "testing" | |
26 | ) | |
27 | ||
28 | type jsonSchemaTest struct { | |
29 | Description string `json:"description"` | |
30 | // Some tests may not always pass, so some tests are manually edited to include | |
31 | // an extra attribute whether that specific test should be disabled and skipped | |
32 | Disabled bool `json:"disabled"` | |
33 | Schema interface{} `json:"schema"` | |
34 | Tests []jsonSchemaTestCase `json:"tests"` | |
35 | } | |
36 | type jsonSchemaTestCase struct { | |
37 | Description string `json:"description"` | |
38 | Data interface{} `json:"data"` | |
39 | Valid bool `json:"valid"` | |
40 | } | |
41 | ||
42 | //Skip any directories not named appropiately | |
43 | // filepath.Walk will also visit files in the root of the test directory | |
44 | var testDirectories = regexp.MustCompile(`(draft\d+)`) | |
45 | var draftMapping = map[string]Draft{ | |
46 | "draft4": Draft4, | |
47 | "draft6": Draft6, | |
48 | "draft7": Draft7, | |
49 | } | |
50 | ||
51 | func executeTests(t *testing.T, path string) error { | |
52 | file, err := os.Open(path) | |
53 | if err != nil { | |
54 | t.Errorf("Error (%s)\n", err.Error()) | |
55 | } | |
56 | fmt.Println(file.Name()) | |
57 | ||
58 | var tests []jsonSchemaTest | |
59 | d := json.NewDecoder(file) | |
60 | d.UseNumber() | |
61 | err = d.Decode(&tests) | |
62 | ||
63 | if err != nil { | |
64 | t.Errorf("Error (%s)\n", err.Error()) | |
65 | } | |
66 | ||
67 | draft := Hybrid | |
68 | if m := testDirectories.FindString(path); m != "" { | |
69 | draft = draftMapping[m] | |
70 | } | |
71 | ||
72 | for _, test := range tests { | |
73 | fmt.Println(" " + test.Description) | |
74 | ||
75 | if test.Disabled { | |
76 | continue | |
77 | } | |
78 | ||
79 | testSchemaLoader := NewRawLoader(test.Schema) | |
80 | sl := NewSchemaLoader() | |
81 | sl.Draft = draft | |
82 | sl.Validate = true | |
83 | testSchema, err := sl.Compile(testSchemaLoader) | |
84 | ||
85 | if err != nil { | |
86 | t.Errorf("Error (%s)\n", err.Error()) | |
87 | } | |
88 | ||
89 | for _, testCase := range test.Tests { | |
90 | testDataLoader := NewRawLoader(testCase.Data) | |
91 | result, err := testSchema.Validate(testDataLoader) | |
92 | ||
93 | if err != nil { | |
94 | t.Errorf("Error (%s)\n", err.Error()) | |
95 | } | |
96 | ||
97 | if result.Valid() != testCase.Valid { | |
98 | schemaString, _ := marshalToJSONString(test.Schema) | |
99 | testCaseString, _ := marshalToJSONString(testCase.Data) | |
100 | ||
101 | t.Errorf("Test failed : %s\n"+ | |
102 | "%s.\n"+ | |
103 | "%s.\n"+ | |
104 | "expects: %t, given %t\n"+ | |
105 | "Schema: %s\n"+ | |
106 | "Data: %s\n", | |
107 | file.Name(), | |
108 | test.Description, | |
109 | testCase.Description, | |
110 | testCase.Valid, | |
111 | result.Valid(), | |
112 | *schemaString, | |
113 | *testCaseString) | |
114 | } | |
115 | } | |
116 | } | |
117 | return nil | |
118 | } | |
119 | ||
120 | func TestSuite(t *testing.T) { | |
121 | ||
122 | wd, err := os.Getwd() | |
123 | if err != nil { | |
124 | panic(err.Error()) | |
125 | } | |
126 | wd = filepath.Join(wd, "testdata") | |
127 | ||
128 | go func() { | |
129 | err := http.ListenAndServe(":1234", http.FileServer(http.Dir(filepath.Join(wd, "remotes")))) | |
130 | if err != nil { | |
131 | ||
132 | panic(err.Error()) | |
133 | } | |
134 | }() | |
135 | ||
136 | err = filepath.Walk(wd, func(path string, fileInfo os.FileInfo, err error) error { | |
137 | if fileInfo.IsDir() && path != wd && !testDirectories.MatchString(fileInfo.Name()) { | |
138 | return filepath.SkipDir | |
139 | } | |
140 | if !strings.HasSuffix(fileInfo.Name(), ".json") { | |
141 | return nil | |
142 | } | |
143 | return executeTests(t, path) | |
144 | }) | |
145 | if err != nil { | |
146 | t.Errorf("Error (%s)\n", err.Error()) | |
147 | } | |
148 | } | |
149 | ||
150 | func TestFormats(t *testing.T) { | |
151 | wd, err := os.Getwd() | |
152 | if err != nil { | |
153 | panic(err.Error()) | |
154 | } | |
155 | wd = filepath.Join(wd, "testdata") | |
156 | ||
157 | dirs, err := ioutil.ReadDir(wd) | |
158 | ||
159 | if err != nil { | |
160 | panic(err.Error()) | |
161 | } | |
162 | ||
163 | for _, dir := range dirs { | |
164 | if testDirectories.MatchString(dir.Name()) { | |
165 | formatsDirectory := filepath.Join(wd, dir.Name(), "optional", "format") | |
166 | err = filepath.Walk(formatsDirectory, func(path string, fileInfo os.FileInfo, err error) error { | |
167 | if fileInfo == nil || !strings.HasSuffix(fileInfo.Name(), ".json") { | |
168 | return nil | |
169 | } | |
170 | return executeTests(t, path) | |
171 | }) | |
172 | ||
173 | if err != nil { | |
174 | t.Errorf("Error (%s)\n", err.Error()) | |
175 | } | |
176 | } | |
177 | } | |
178 | } |
25 | 25 | package gojsonschema |
26 | 26 | |
27 | 27 | type ( |
28 | // locale is an interface for definining custom error strings | |
28 | // locale is an interface for defining custom error strings | |
29 | 29 | locale interface { |
30 | ||
31 | // False returns a format-string for "false" schema validation errors | |
32 | False() string | |
33 | ||
34 | // Required returns a format-string for "required" schema validation errors | |
30 | 35 | Required() string |
36 | ||
37 | // InvalidType returns a format-string for "invalid type" schema validation errors | |
31 | 38 | InvalidType() string |
39 | ||
40 | // NumberAnyOf returns a format-string for "anyOf" schema validation errors | |
32 | 41 | NumberAnyOf() string |
42 | ||
43 | // NumberOneOf returns a format-string for "oneOf" schema validation errors | |
33 | 44 | NumberOneOf() string |
45 | ||
46 | // NumberAllOf returns a format-string for "allOf" schema validation errors | |
34 | 47 | NumberAllOf() string |
48 | ||
49 | // NumberNot returns a format-string to format a NumberNotError | |
35 | 50 | NumberNot() string |
51 | ||
52 | // MissingDependency returns a format-string for "missing dependency" schema validation errors | |
36 | 53 | MissingDependency() string |
54 | ||
55 | // Internal returns a format-string for internal errors | |
37 | 56 | Internal() string |
57 | ||
58 | // Const returns a format-string to format a ConstError | |
59 | Const() string | |
60 | ||
61 | // Enum returns a format-string to format an EnumError | |
38 | 62 | Enum() string |
63 | ||
64 | // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema | |
39 | 65 | ArrayNotEnoughItems() string |
66 | ||
67 | // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError | |
40 | 68 | ArrayNoAdditionalItems() string |
69 | ||
70 | // ArrayMinItems returns a format-string to format an ArrayMinItemsError | |
41 | 71 | ArrayMinItems() string |
72 | ||
73 | // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError | |
42 | 74 | ArrayMaxItems() string |
75 | ||
76 | // Unique returns a format-string to format an ItemsMustBeUniqueError | |
43 | 77 | Unique() string |
78 | ||
79 | // ArrayContains returns a format-string to format an ArrayContainsError | |
80 | ArrayContains() string | |
81 | ||
82 | // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError | |
44 | 83 | ArrayMinProperties() string |
84 | ||
85 | // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError | |
45 | 86 | ArrayMaxProperties() string |
87 | ||
88 | // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError | |
46 | 89 | AdditionalPropertyNotAllowed() string |
90 | ||
91 | // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError | |
47 | 92 | InvalidPropertyPattern() string |
93 | ||
94 | // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError | |
95 | InvalidPropertyName() string | |
96 | ||
97 | // StringGTE returns a format-string to format an StringLengthGTEError | |
48 | 98 | StringGTE() string |
99 | ||
100 | // StringLTE returns a format-string to format an StringLengthLTEError | |
49 | 101 | StringLTE() string |
102 | ||
103 | // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError | |
50 | 104 | DoesNotMatchPattern() string |
105 | ||
106 | // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError | |
51 | 107 | DoesNotMatchFormat() string |
108 | ||
109 | // MultipleOf returns a format-string to format an MultipleOfError | |
52 | 110 | MultipleOf() string |
111 | ||
112 | // NumberGTE returns a format-string to format an NumberGTEError | |
53 | 113 | NumberGTE() string |
114 | ||
115 | // NumberGT returns a format-string to format an NumberGTError | |
54 | 116 | NumberGT() string |
117 | ||
118 | // NumberLTE returns a format-string to format an NumberLTEError | |
55 | 119 | NumberLTE() string |
120 | ||
121 | // NumberLT returns a format-string to format an NumberLTError | |
56 | 122 | NumberLT() string |
57 | 123 | |
58 | 124 | // Schema validations |
125 | ||
126 | // RegexPattern returns a format-string to format a regex-pattern error | |
59 | 127 | RegexPattern() string |
128 | ||
129 | // GreaterThanZero returns a format-string to format an error where a number must be greater than zero | |
60 | 130 | GreaterThanZero() string |
131 | ||
132 | // MustBeOfA returns a format-string to format an error where a value is of the wrong type | |
61 | 133 | MustBeOfA() string |
134 | ||
135 | // MustBeOfAn returns a format-string to format an error where a value is of the wrong type | |
62 | 136 | MustBeOfAn() string |
137 | ||
138 | // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error | |
63 | 139 | CannotBeUsedWithout() string |
140 | ||
141 | // CannotBeGT returns a format-string to format an error where a value are greater than allowed | |
64 | 142 | CannotBeGT() string |
143 | ||
144 | // MustBeOfType returns a format-string to format an error where a value does not match the required type | |
65 | 145 | MustBeOfType() string |
146 | ||
147 | // MustBeValidRegex returns a format-string to format an error where a regex is invalid | |
66 | 148 | MustBeValidRegex() string |
149 | ||
150 | // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format | |
67 | 151 | MustBeValidFormat() string |
152 | ||
153 | // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 | |
68 | 154 | MustBeGTEZero() string |
155 | ||
156 | // KeyCannotBeGreaterThan returns a format-string to format an error where a key is greater than the maximum allowed | |
69 | 157 | KeyCannotBeGreaterThan() string |
158 | ||
159 | // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type | |
70 | 160 | KeyItemsMustBeOfType() string |
161 | ||
162 | // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique | |
71 | 163 | KeyItemsMustBeUnique() string |
164 | ||
165 | // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error | |
72 | 166 | ReferenceMustBeCanonical() string |
167 | ||
168 | // NotAValidType returns a format-string to format an invalid type error | |
73 | 169 | NotAValidType() string |
170 | ||
171 | // Duplicated returns a format-string to format an error where types are duplicated | |
74 | 172 | Duplicated() string |
75 | httpBadStatus() string | |
76 | ||
77 | // ErrorFormat | |
173 | ||
174 | // HttpBadStatus returns a format-string for errors when loading a schema using HTTP | |
175 | HttpBadStatus() string | |
176 | ||
177 | // ParseError returns a format-string for JSON parsing errors | |
178 | ParseError() string | |
179 | ||
180 | // ConditionThen returns a format-string for ConditionThenError errors | |
181 | ConditionThen() string | |
182 | ||
183 | // ConditionElse returns a format-string for ConditionElseError errors | |
184 | ConditionElse() string | |
185 | ||
186 | // ErrorFormat returns a format string for errors | |
78 | 187 | ErrorFormat() string |
79 | 188 | } |
80 | 189 | |
82 | 191 | DefaultLocale struct{} |
83 | 192 | ) |
84 | 193 | |
194 | // False returns a format-string for "false" schema validation errors | |
195 | func (l DefaultLocale) False() string { | |
196 | return "False always fails validation" | |
197 | } | |
198 | ||
199 | // Required returns a format-string for "required" schema validation errors | |
85 | 200 | func (l DefaultLocale) Required() string { |
86 | 201 | return `{{.property}} is required` |
87 | 202 | } |
88 | 203 | |
204 | // InvalidType returns a format-string for "invalid type" schema validation errors | |
89 | 205 | func (l DefaultLocale) InvalidType() string { |
90 | 206 | return `Invalid type. Expected: {{.expected}}, given: {{.given}}` |
91 | 207 | } |
92 | 208 | |
209 | // NumberAnyOf returns a format-string for "anyOf" schema validation errors | |
93 | 210 | func (l DefaultLocale) NumberAnyOf() string { |
94 | 211 | return `Must validate at least one schema (anyOf)` |
95 | 212 | } |
96 | 213 | |
214 | // NumberOneOf returns a format-string for "oneOf" schema validation errors | |
97 | 215 | func (l DefaultLocale) NumberOneOf() string { |
98 | 216 | return `Must validate one and only one schema (oneOf)` |
99 | 217 | } |
100 | 218 | |
219 | // NumberAllOf returns a format-string for "allOf" schema validation errors | |
101 | 220 | func (l DefaultLocale) NumberAllOf() string { |
102 | 221 | return `Must validate all the schemas (allOf)` |
103 | 222 | } |
104 | 223 | |
224 | // NumberNot returns a format-string to format a NumberNotError | |
105 | 225 | func (l DefaultLocale) NumberNot() string { |
106 | 226 | return `Must not validate the schema (not)` |
107 | 227 | } |
108 | 228 | |
229 | // MissingDependency returns a format-string for "missing dependency" schema validation errors | |
109 | 230 | func (l DefaultLocale) MissingDependency() string { |
110 | 231 | return `Has a dependency on {{.dependency}}` |
111 | 232 | } |
112 | 233 | |
234 | // Internal returns a format-string for internal errors | |
113 | 235 | func (l DefaultLocale) Internal() string { |
114 | 236 | return `Internal Error {{.error}}` |
115 | 237 | } |
116 | 238 | |
239 | // Const returns a format-string to format a ConstError | |
240 | func (l DefaultLocale) Const() string { | |
241 | return `{{.field}} does not match: {{.allowed}}` | |
242 | } | |
243 | ||
244 | // Enum returns a format-string to format an EnumError | |
117 | 245 | func (l DefaultLocale) Enum() string { |
118 | 246 | return `{{.field}} must be one of the following: {{.allowed}}` |
119 | 247 | } |
120 | 248 | |
249 | // ArrayNoAdditionalItems returns a format-string to format an ArrayNoAdditionalItemsError | |
121 | 250 | func (l DefaultLocale) ArrayNoAdditionalItems() string { |
122 | 251 | return `No additional items allowed on array` |
123 | 252 | } |
124 | 253 | |
254 | // ArrayNotEnoughItems returns a format-string to format an error for arrays having not enough items to match positional list of schema | |
125 | 255 | func (l DefaultLocale) ArrayNotEnoughItems() string { |
126 | 256 | return `Not enough items on array to match positional list of schema` |
127 | 257 | } |
128 | 258 | |
259 | // ArrayMinItems returns a format-string to format an ArrayMinItemsError | |
129 | 260 | func (l DefaultLocale) ArrayMinItems() string { |
130 | 261 | return `Array must have at least {{.min}} items` |
131 | 262 | } |
132 | 263 | |
264 | // ArrayMaxItems returns a format-string to format an ArrayMaxItemsError | |
133 | 265 | func (l DefaultLocale) ArrayMaxItems() string { |
134 | 266 | return `Array must have at most {{.max}} items` |
135 | 267 | } |
136 | 268 | |
269 | // Unique returns a format-string to format an ItemsMustBeUniqueError | |
137 | 270 | func (l DefaultLocale) Unique() string { |
138 | return `{{.type}} items must be unique` | |
139 | } | |
140 | ||
271 | return `{{.type}} items[{{.i}},{{.j}}] must be unique` | |
272 | } | |
273 | ||
274 | // ArrayContains returns a format-string to format an ArrayContainsError | |
275 | func (l DefaultLocale) ArrayContains() string { | |
276 | return `At least one of the items must match` | |
277 | } | |
278 | ||
279 | // ArrayMinProperties returns a format-string to format an ArrayMinPropertiesError | |
141 | 280 | func (l DefaultLocale) ArrayMinProperties() string { |
142 | 281 | return `Must have at least {{.min}} properties` |
143 | 282 | } |
144 | 283 | |
284 | // ArrayMaxProperties returns a format-string to format an ArrayMaxPropertiesError | |
145 | 285 | func (l DefaultLocale) ArrayMaxProperties() string { |
146 | 286 | return `Must have at most {{.max}} properties` |
147 | 287 | } |
148 | 288 | |
289 | // AdditionalPropertyNotAllowed returns a format-string to format an AdditionalPropertyNotAllowedError | |
149 | 290 | func (l DefaultLocale) AdditionalPropertyNotAllowed() string { |
150 | 291 | return `Additional property {{.property}} is not allowed` |
151 | 292 | } |
152 | 293 | |
294 | // InvalidPropertyPattern returns a format-string to format an InvalidPropertyPatternError | |
153 | 295 | func (l DefaultLocale) InvalidPropertyPattern() string { |
154 | 296 | return `Property "{{.property}}" does not match pattern {{.pattern}}` |
155 | 297 | } |
156 | 298 | |
299 | // InvalidPropertyName returns a format-string to format an InvalidPropertyNameError | |
300 | func (l DefaultLocale) InvalidPropertyName() string { | |
301 | return `Property name of "{{.property}}" does not match` | |
302 | } | |
303 | ||
304 | // StringGTE returns a format-string to format an StringLengthGTEError | |
157 | 305 | func (l DefaultLocale) StringGTE() string { |
158 | 306 | return `String length must be greater than or equal to {{.min}}` |
159 | 307 | } |
160 | 308 | |
309 | // StringLTE returns a format-string to format an StringLengthLTEError | |
161 | 310 | func (l DefaultLocale) StringLTE() string { |
162 | 311 | return `String length must be less than or equal to {{.max}}` |
163 | 312 | } |
164 | 313 | |
314 | // DoesNotMatchPattern returns a format-string to format an DoesNotMatchPatternError | |
165 | 315 | func (l DefaultLocale) DoesNotMatchPattern() string { |
166 | 316 | return `Does not match pattern '{{.pattern}}'` |
167 | 317 | } |
168 | 318 | |
319 | // DoesNotMatchFormat returns a format-string to format an DoesNotMatchFormatError | |
169 | 320 | func (l DefaultLocale) DoesNotMatchFormat() string { |
170 | 321 | return `Does not match format '{{.format}}'` |
171 | 322 | } |
172 | 323 | |
324 | // MultipleOf returns a format-string to format an MultipleOfError | |
173 | 325 | func (l DefaultLocale) MultipleOf() string { |
174 | 326 | return `Must be a multiple of {{.multiple}}` |
175 | 327 | } |
176 | 328 | |
329 | // NumberGTE returns the format string to format a NumberGTEError | |
177 | 330 | func (l DefaultLocale) NumberGTE() string { |
178 | 331 | return `Must be greater than or equal to {{.min}}` |
179 | 332 | } |
180 | 333 | |
334 | // NumberGT returns the format string to format a NumberGTError | |
181 | 335 | func (l DefaultLocale) NumberGT() string { |
182 | 336 | return `Must be greater than {{.min}}` |
183 | 337 | } |
184 | 338 | |
339 | // NumberLTE returns the format string to format a NumberLTEError | |
185 | 340 | func (l DefaultLocale) NumberLTE() string { |
186 | 341 | return `Must be less than or equal to {{.max}}` |
187 | 342 | } |
188 | 343 | |
344 | // NumberLT returns the format string to format a NumberLTError | |
189 | 345 | func (l DefaultLocale) NumberLT() string { |
190 | 346 | return `Must be less than {{.max}}` |
191 | 347 | } |
192 | 348 | |
193 | 349 | // Schema validators |
350 | ||
351 | // RegexPattern returns a format-string to format a regex-pattern error | |
194 | 352 | func (l DefaultLocale) RegexPattern() string { |
195 | 353 | return `Invalid regex pattern '{{.pattern}}'` |
196 | 354 | } |
197 | 355 | |
356 | // GreaterThanZero returns a format-string to format an error where a number must be greater than zero | |
198 | 357 | func (l DefaultLocale) GreaterThanZero() string { |
199 | 358 | return `{{.number}} must be strictly greater than 0` |
200 | 359 | } |
201 | 360 | |
361 | // MustBeOfA returns a format-string to format an error where a value is of the wrong type | |
202 | 362 | func (l DefaultLocale) MustBeOfA() string { |
203 | 363 | return `{{.x}} must be of a {{.y}}` |
204 | 364 | } |
205 | 365 | |
366 | // MustBeOfAn returns a format-string to format an error where a value is of the wrong type | |
206 | 367 | func (l DefaultLocale) MustBeOfAn() string { |
207 | 368 | return `{{.x}} must be of an {{.y}}` |
208 | 369 | } |
209 | 370 | |
371 | // CannotBeUsedWithout returns a format-string to format a "cannot be used without" error | |
210 | 372 | func (l DefaultLocale) CannotBeUsedWithout() string { |
211 | 373 | return `{{.x}} cannot be used without {{.y}}` |
212 | 374 | } |
213 | 375 | |
376 | // CannotBeGT returns a format-string to format an error where a value are greater than allowed | |
214 | 377 | func (l DefaultLocale) CannotBeGT() string { |
215 | 378 | return `{{.x}} cannot be greater than {{.y}}` |
216 | 379 | } |
217 | 380 | |
381 | // MustBeOfType returns a format-string to format an error where a value does not match the required type | |
218 | 382 | func (l DefaultLocale) MustBeOfType() string { |
219 | 383 | return `{{.key}} must be of type {{.type}}` |
220 | 384 | } |
221 | 385 | |
386 | // MustBeValidRegex returns a format-string to format an error where a regex is invalid | |
222 | 387 | func (l DefaultLocale) MustBeValidRegex() string { |
223 | 388 | return `{{.key}} must be a valid regex` |
224 | 389 | } |
225 | 390 | |
391 | // MustBeValidFormat returns a format-string to format an error where a value does not match the expected format | |
226 | 392 | func (l DefaultLocale) MustBeValidFormat() string { |
227 | 393 | return `{{.key}} must be a valid format {{.given}}` |
228 | 394 | } |
229 | 395 | |
396 | // MustBeGTEZero returns a format-string to format an error where a value must be greater or equal than 0 | |
230 | 397 | func (l DefaultLocale) MustBeGTEZero() string { |
231 | 398 | return `{{.key}} must be greater than or equal to 0` |
232 | 399 | } |
233 | 400 | |
401 | // KeyCannotBeGreaterThan returns a format-string to format an error where a value is greater than the maximum allowed | |
234 | 402 | func (l DefaultLocale) KeyCannotBeGreaterThan() string { |
235 | 403 | return `{{.key}} cannot be greater than {{.y}}` |
236 | 404 | } |
237 | 405 | |
406 | // KeyItemsMustBeOfType returns a format-string to format an error where a key is of the wrong type | |
238 | 407 | func (l DefaultLocale) KeyItemsMustBeOfType() string { |
239 | 408 | return `{{.key}} items must be {{.type}}` |
240 | 409 | } |
241 | 410 | |
411 | // KeyItemsMustBeUnique returns a format-string to format an error where keys are not unique | |
242 | 412 | func (l DefaultLocale) KeyItemsMustBeUnique() string { |
243 | 413 | return `{{.key}} items must be unique` |
244 | 414 | } |
245 | 415 | |
416 | // ReferenceMustBeCanonical returns a format-string to format a "reference must be canonical" error | |
246 | 417 | func (l DefaultLocale) ReferenceMustBeCanonical() string { |
247 | 418 | return `Reference {{.reference}} must be canonical` |
248 | 419 | } |
249 | 420 | |
421 | // NotAValidType returns a format-string to format an invalid type error | |
250 | 422 | func (l DefaultLocale) NotAValidType() string { |
251 | return `{{.type}} is not a valid type -- ` | |
252 | } | |
253 | ||
423 | return `has a primitive type that is NOT VALID -- given: {{.given}} Expected valid values are:{{.expected}}` | |
424 | } | |
425 | ||
426 | // Duplicated returns a format-string to format an error where types are duplicated | |
254 | 427 | func (l DefaultLocale) Duplicated() string { |
255 | 428 | return `{{.type}} type is duplicated` |
256 | 429 | } |
257 | 430 | |
258 | func (l DefaultLocale) httpBadStatus() string { | |
431 | // HttpBadStatus returns a format-string for errors when loading a schema using HTTP | |
432 | func (l DefaultLocale) HttpBadStatus() string { | |
259 | 433 | return `Could not read schema from HTTP, response status is {{.status}}` |
260 | 434 | } |
261 | 435 | |
436 | // ErrorFormat returns a format string for errors | |
262 | 437 | // Replacement options: field, description, context, value |
263 | 438 | func (l DefaultLocale) ErrorFormat() string { |
264 | 439 | return `{{.field}}: {{.description}}` |
265 | 440 | } |
266 | 441 | |
442 | // ParseError returns a format-string for JSON parsing errors | |
443 | func (l DefaultLocale) ParseError() string { | |
444 | return `Expected: {{.expected}}, given: Invalid JSON` | |
445 | } | |
446 | ||
447 | // ConditionThen returns a format-string for ConditionThenError errors | |
448 | // If/Else | |
449 | func (l DefaultLocale) ConditionThen() string { | |
450 | return `Must validate "then" as "if" was valid` | |
451 | } | |
452 | ||
453 | // ConditionElse returns a format-string for ConditionElseError errors | |
454 | func (l DefaultLocale) ConditionElse() string { | |
455 | return `Must validate "else" as "if" was not valid` | |
456 | } | |
457 | ||
458 | // constants | |
267 | 459 | const ( |
268 | 460 | STRING_NUMBER = "number" |
269 | 461 | STRING_ARRAY_OF_STRINGS = "array of strings" |
270 | 462 | STRING_ARRAY_OF_SCHEMAS = "array of schemas" |
271 | STRING_SCHEMA = "schema" | |
463 | STRING_SCHEMA = "valid schema" | |
272 | 464 | STRING_SCHEMA_OR_ARRAY_OF_STRINGS = "schema or array of strings" |
273 | 465 | STRING_PROPERTIES = "properties" |
274 | 466 | STRING_DEPENDENCY = "dependency" |
36 | 36 | |
37 | 37 | // ResultError is the interface that library errors must implement |
38 | 38 | ResultError interface { |
39 | // Field returns the field name without the root context | |
40 | // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName | |
39 | 41 | Field() string |
42 | // SetType sets the error-type | |
40 | 43 | SetType(string) |
44 | // Type returns the error-type | |
41 | 45 | Type() string |
42 | SetContext(*jsonContext) | |
43 | Context() *jsonContext | |
46 | // SetContext sets the JSON-context for the error | |
47 | SetContext(*JsonContext) | |
48 | // Context returns the JSON-context of the error | |
49 | Context() *JsonContext | |
50 | // SetDescription sets a description for the error | |
44 | 51 | SetDescription(string) |
52 | // Description returns the description of the error | |
45 | 53 | Description() string |
54 | // SetDescriptionFormat sets the format for the description in the default text/template format | |
55 | SetDescriptionFormat(string) | |
56 | // DescriptionFormat returns the format for the description in the default text/template format | |
57 | DescriptionFormat() string | |
58 | // SetValue sets the value related to the error | |
46 | 59 | SetValue(interface{}) |
60 | // Value returns the value related to the error | |
47 | 61 | Value() interface{} |
62 | // SetDetails sets the details specific to the error | |
48 | 63 | SetDetails(ErrorDetails) |
64 | // Details returns details about the error | |
49 | 65 | Details() ErrorDetails |
66 | // String returns a string representation of the error | |
50 | 67 | String() string |
51 | 68 | } |
52 | 69 | |
54 | 71 | // ResultErrorFields implements the ResultError interface, so custom errors |
55 | 72 | // can be defined by just embedding this type |
56 | 73 | ResultErrorFields struct { |
57 | errorType string // A string with the type of error (i.e. invalid_type) | |
58 | context *jsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ... | |
59 | description string // A human readable error message | |
60 | value interface{} // Value given by the JSON file that is the source of the error | |
61 | details ErrorDetails | |
62 | } | |
63 | ||
74 | errorType string // A string with the type of error (i.e. invalid_type) | |
75 | context *JsonContext // Tree like notation of the part that failed the validation. ex (root).a.b ... | |
76 | description string // A human readable error message | |
77 | descriptionFormat string // A format for human readable error message | |
78 | value interface{} // Value given by the JSON file that is the source of the error | |
79 | details ErrorDetails | |
80 | } | |
81 | ||
82 | // Result holds the result of a validation | |
64 | 83 | Result struct { |
65 | 84 | errors []ResultError |
66 | 85 | // Scores how well the validation matched. Useful in generating |
69 | 88 | } |
70 | 89 | ) |
71 | 90 | |
72 | // Field outputs the field name without the root context | |
91 | // Field returns the field name without the root context | |
73 | 92 | // i.e. firstName or person.firstName instead of (root).firstName or (root).person.firstName |
74 | 93 | func (v *ResultErrorFields) Field() string { |
75 | if p, ok := v.Details()["property"]; ok { | |
76 | if str, isString := p.(string); isString { | |
77 | return str | |
78 | } | |
79 | } | |
80 | ||
81 | 94 | return strings.TrimPrefix(v.context.String(), STRING_ROOT_SCHEMA_PROPERTY+".") |
82 | 95 | } |
83 | 96 | |
97 | // SetType sets the error-type | |
84 | 98 | func (v *ResultErrorFields) SetType(errorType string) { |
85 | 99 | v.errorType = errorType |
86 | 100 | } |
87 | 101 | |
102 | // Type returns the error-type | |
88 | 103 | func (v *ResultErrorFields) Type() string { |
89 | 104 | return v.errorType |
90 | 105 | } |
91 | 106 | |
92 | func (v *ResultErrorFields) SetContext(context *jsonContext) { | |
107 | // SetContext sets the JSON-context for the error | |
108 | func (v *ResultErrorFields) SetContext(context *JsonContext) { | |
93 | 109 | v.context = context |
94 | 110 | } |
95 | 111 | |
96 | func (v *ResultErrorFields) Context() *jsonContext { | |
112 | // Context returns the JSON-context of the error | |
113 | func (v *ResultErrorFields) Context() *JsonContext { | |
97 | 114 | return v.context |
98 | 115 | } |
99 | 116 | |
117 | // SetDescription sets a description for the error | |
100 | 118 | func (v *ResultErrorFields) SetDescription(description string) { |
101 | 119 | v.description = description |
102 | 120 | } |
103 | 121 | |
122 | // Description returns the description of the error | |
104 | 123 | func (v *ResultErrorFields) Description() string { |
105 | 124 | return v.description |
106 | 125 | } |
107 | 126 | |
127 | // SetDescriptionFormat sets the format for the description in the default text/template format | |
128 | func (v *ResultErrorFields) SetDescriptionFormat(descriptionFormat string) { | |
129 | v.descriptionFormat = descriptionFormat | |
130 | } | |
131 | ||
132 | // DescriptionFormat returns the format for the description in the default text/template format | |
133 | func (v *ResultErrorFields) DescriptionFormat() string { | |
134 | return v.descriptionFormat | |
135 | } | |
136 | ||
137 | // SetValue sets the value related to the error | |
108 | 138 | func (v *ResultErrorFields) SetValue(value interface{}) { |
109 | 139 | v.value = value |
110 | 140 | } |
111 | 141 | |
142 | // Value returns the value related to the error | |
112 | 143 | func (v *ResultErrorFields) Value() interface{} { |
113 | 144 | return v.value |
114 | 145 | } |
115 | 146 | |
147 | // SetDetails sets the details specific to the error | |
116 | 148 | func (v *ResultErrorFields) SetDetails(details ErrorDetails) { |
117 | 149 | v.details = details |
118 | 150 | } |
119 | 151 | |
152 | // Details returns details about the error | |
120 | 153 | func (v *ResultErrorFields) Details() ErrorDetails { |
121 | 154 | return v.details |
122 | 155 | } |
123 | 156 | |
157 | // String returns a string representation of the error | |
124 | 158 | func (v ResultErrorFields) String() string { |
125 | 159 | // as a fallback, the value is displayed go style |
126 | 160 | valueString := fmt.Sprintf("%v", v.value) |
129 | 163 | if v.value == nil { |
130 | 164 | valueString = TYPE_NULL |
131 | 165 | } else { |
132 | if vs, err := marshalToJsonString(v.value); err == nil { | |
166 | if vs, err := marshalToJSONString(v.value); err == nil { | |
133 | 167 | if vs == nil { |
134 | 168 | valueString = TYPE_NULL |
135 | 169 | } else { |
146 | 180 | }) |
147 | 181 | } |
148 | 182 | |
183 | // Valid indicates if no errors were found | |
149 | 184 | func (v *Result) Valid() bool { |
150 | 185 | return len(v.errors) == 0 |
151 | 186 | } |
152 | 187 | |
188 | // Errors returns the errors that were found | |
153 | 189 | func (v *Result) Errors() []ResultError { |
154 | 190 | return v.errors |
155 | 191 | } |
156 | 192 | |
157 | func (v *Result) addError(err ResultError, context *jsonContext, value interface{}, details ErrorDetails) { | |
193 | // AddError appends a fully filled error to the error set | |
194 | // SetDescription() will be called with the result of the parsed err.DescriptionFormat() | |
195 | func (v *Result) AddError(err ResultError, details ErrorDetails) { | |
196 | if _, exists := details["context"]; !exists && err.Context() != nil { | |
197 | details["context"] = err.Context().String() | |
198 | } | |
199 | ||
200 | err.SetDescription(formatErrorDescription(err.DescriptionFormat(), details)) | |
201 | ||
202 | v.errors = append(v.errors, err) | |
203 | } | |
204 | ||
205 | func (v *Result) addInternalError(err ResultError, context *JsonContext, value interface{}, details ErrorDetails) { | |
158 | 206 | newError(err, context, value, Locale, details) |
159 | 207 | v.errors = append(v.errors, err) |
160 | 208 | v.score -= 2 // results in a net -1 when added to the +1 we get at the end of the validation function |
26 | 26 | package gojsonschema |
27 | 27 | |
28 | 28 | import ( |
29 | // "encoding/json" | |
30 | 29 | "errors" |
30 | "math/big" | |
31 | 31 | "reflect" |
32 | 32 | "regexp" |
33 | "text/template" | |
33 | 34 | |
34 | 35 | "github.com/xeipuuv/gojsonreference" |
35 | 36 | ) |
38 | 39 | // Locale is the default locale to use |
39 | 40 | // Library users can overwrite with their own implementation |
40 | 41 | Locale locale = DefaultLocale{} |
42 | ||
43 | // ErrorTemplateFuncs allows you to define custom template funcs for use in localization. | |
44 | ErrorTemplateFuncs template.FuncMap | |
41 | 45 | ) |
42 | 46 | |
47 | // NewSchema instances a schema using the given JSONLoader | |
43 | 48 | func NewSchema(l JSONLoader) (*Schema, error) { |
44 | ref, err := l.JsonReference() | |
45 | if err != nil { | |
46 | return nil, err | |
47 | } | |
48 | ||
49 | d := Schema{} | |
50 | d.pool = newSchemaPool(l.LoaderFactory()) | |
51 | d.documentReference = ref | |
52 | d.referencePool = newSchemaReferencePool() | |
53 | ||
54 | var doc interface{} | |
55 | if ref.String() != "" { | |
56 | // Get document from schema pool | |
57 | spd, err := d.pool.GetDocument(d.documentReference) | |
58 | if err != nil { | |
59 | return nil, err | |
60 | } | |
61 | doc = spd.Document | |
62 | } else { | |
63 | // Load JSON directly | |
64 | doc, err = l.LoadJSON() | |
65 | if err != nil { | |
66 | return nil, err | |
67 | } | |
68 | d.pool.SetStandaloneDocument(doc) | |
69 | } | |
70 | ||
71 | err = d.parse(doc) | |
72 | if err != nil { | |
73 | return nil, err | |
74 | } | |
75 | ||
76 | return &d, nil | |
49 | return NewSchemaLoader().Compile(l) | |
77 | 50 | } |
78 | 51 | |
52 | // Schema holds a schema | |
79 | 53 | type Schema struct { |
80 | 54 | documentReference gojsonreference.JsonReference |
81 | 55 | rootSchema *subSchema |
83 | 57 | referencePool *schemaReferencePool |
84 | 58 | } |
85 | 59 | |
86 | func (d *Schema) parse(document interface{}) error { | |
87 | d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY} | |
60 | func (d *Schema) parse(document interface{}, draft Draft) error { | |
61 | d.rootSchema = &subSchema{property: STRING_ROOT_SCHEMA_PROPERTY, draft: &draft} | |
88 | 62 | return d.parseSchema(document, d.rootSchema) |
89 | 63 | } |
90 | 64 | |
65 | // SetRootSchemaName sets the root-schema name | |
91 | 66 | func (d *Schema) SetRootSchemaName(name string) { |
92 | 67 | d.rootSchema.property = name |
93 | 68 | } |
100 | 75 | // |
101 | 76 | func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema) error { |
102 | 77 | |
78 | if currentSchema.draft == nil { | |
79 | if currentSchema.parent == nil { | |
80 | return errors.New("Draft not set") | |
81 | } | |
82 | currentSchema.draft = currentSchema.parent.draft | |
83 | } | |
84 | ||
85 | // As of draft 6 "true" is equivalent to an empty schema "{}" and false equals "{"not":{}}" | |
86 | if *currentSchema.draft >= Draft6 && isKind(documentNode, reflect.Bool) { | |
87 | b := documentNode.(bool) | |
88 | currentSchema.pass = &b | |
89 | return nil | |
90 | } | |
91 | ||
103 | 92 | if !isKind(documentNode, reflect.Map) { |
93 | return errors.New(formatErrorDescription( | |
94 | Locale.ParseError(), | |
95 | ErrorDetails{ | |
96 | "expected": STRING_SCHEMA, | |
97 | }, | |
98 | )) | |
99 | } | |
100 | ||
101 | m := documentNode.(map[string]interface{}) | |
102 | ||
103 | if currentSchema.parent == nil { | |
104 | currentSchema.ref = &d.documentReference | |
105 | currentSchema.id = &d.documentReference | |
106 | } | |
107 | ||
108 | if currentSchema.id == nil && currentSchema.parent != nil { | |
109 | currentSchema.id = currentSchema.parent.id | |
110 | } | |
111 | ||
112 | // In draft 6 the id keyword was renamed to $id | |
113 | // Hybrid mode uses the old id by default | |
114 | var keyID string | |
115 | ||
116 | switch *currentSchema.draft { | |
117 | case Draft4: | |
118 | keyID = KEY_ID | |
119 | case Hybrid: | |
120 | keyID = KEY_ID_NEW | |
121 | if existsMapKey(m, KEY_ID) { | |
122 | keyID = KEY_ID | |
123 | } | |
124 | default: | |
125 | keyID = KEY_ID_NEW | |
126 | } | |
127 | if existsMapKey(m, keyID) && !isKind(m[keyID], reflect.String) { | |
104 | 128 | return errors.New(formatErrorDescription( |
105 | 129 | Locale.InvalidType(), |
106 | 130 | ErrorDetails{ |
107 | "expected": TYPE_OBJECT, | |
108 | "given": STRING_SCHEMA, | |
131 | "expected": TYPE_STRING, | |
132 | "given": keyID, | |
109 | 133 | }, |
110 | 134 | )) |
111 | 135 | } |
112 | ||
113 | m := documentNode.(map[string]interface{}) | |
114 | ||
115 | if currentSchema == d.rootSchema { | |
116 | currentSchema.ref = &d.documentReference | |
117 | } | |
118 | ||
119 | // $subSchema | |
120 | if existsMapKey(m, KEY_SCHEMA) { | |
121 | if !isKind(m[KEY_SCHEMA], reflect.String) { | |
136 | if k, ok := m[keyID].(string); ok { | |
137 | jsonReference, err := gojsonreference.NewJsonReference(k) | |
138 | if err != nil { | |
139 | return err | |
140 | } | |
141 | if currentSchema == d.rootSchema { | |
142 | currentSchema.id = &jsonReference | |
143 | } else { | |
144 | ref, err := currentSchema.parent.id.Inherits(jsonReference) | |
145 | if err != nil { | |
146 | return err | |
147 | } | |
148 | currentSchema.id = ref | |
149 | } | |
150 | } | |
151 | ||
152 | // definitions | |
153 | if existsMapKey(m, KEY_DEFINITIONS) { | |
154 | if isKind(m[KEY_DEFINITIONS], reflect.Map, reflect.Bool) { | |
155 | for _, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { | |
156 | if isKind(dv, reflect.Map, reflect.Bool) { | |
157 | ||
158 | newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema} | |
159 | ||
160 | err := d.parseSchema(dv, newSchema) | |
161 | ||
162 | if err != nil { | |
163 | return err | |
164 | } | |
165 | } else { | |
166 | return errors.New(formatErrorDescription( | |
167 | Locale.InvalidType(), | |
168 | ErrorDetails{ | |
169 | "expected": STRING_ARRAY_OF_SCHEMAS, | |
170 | "given": KEY_DEFINITIONS, | |
171 | }, | |
172 | )) | |
173 | } | |
174 | } | |
175 | } else { | |
122 | 176 | return errors.New(formatErrorDescription( |
123 | 177 | Locale.InvalidType(), |
124 | 178 | ErrorDetails{ |
125 | "expected": TYPE_STRING, | |
126 | "given": KEY_SCHEMA, | |
179 | "expected": STRING_ARRAY_OF_SCHEMAS, | |
180 | "given": KEY_DEFINITIONS, | |
127 | 181 | }, |
128 | 182 | )) |
129 | 183 | } |
130 | schemaRef := m[KEY_SCHEMA].(string) | |
131 | schemaReference, err := gojsonreference.NewJsonReference(schemaRef) | |
132 | currentSchema.subSchema = &schemaReference | |
133 | if err != nil { | |
134 | return err | |
135 | } | |
184 | ||
185 | } | |
186 | ||
187 | // title | |
188 | if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) { | |
189 | return errors.New(formatErrorDescription( | |
190 | Locale.InvalidType(), | |
191 | ErrorDetails{ | |
192 | "expected": TYPE_STRING, | |
193 | "given": KEY_TITLE, | |
194 | }, | |
195 | )) | |
196 | } | |
197 | if k, ok := m[KEY_TITLE].(string); ok { | |
198 | currentSchema.title = &k | |
199 | } | |
200 | ||
201 | // description | |
202 | if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) { | |
203 | return errors.New(formatErrorDescription( | |
204 | Locale.InvalidType(), | |
205 | ErrorDetails{ | |
206 | "expected": TYPE_STRING, | |
207 | "given": KEY_DESCRIPTION, | |
208 | }, | |
209 | )) | |
210 | } | |
211 | if k, ok := m[KEY_DESCRIPTION].(string); ok { | |
212 | currentSchema.description = &k | |
136 | 213 | } |
137 | 214 | |
138 | 215 | // $ref |
145 | 222 | }, |
146 | 223 | )) |
147 | 224 | } |
225 | ||
148 | 226 | if k, ok := m[KEY_REF].(string); ok { |
149 | 227 | |
150 | 228 | jsonReference, err := gojsonreference.NewJsonReference(k) |
152 | 230 | return err |
153 | 231 | } |
154 | 232 | |
155 | if jsonReference.HasFullUrl { | |
156 | currentSchema.ref = &jsonReference | |
157 | } else { | |
158 | inheritedReference, err := currentSchema.ref.Inherits(jsonReference) | |
233 | currentSchema.ref = &jsonReference | |
234 | ||
235 | if sch, ok := d.referencePool.Get(currentSchema.ref.String()); ok { | |
236 | currentSchema.refSchema = sch | |
237 | } else { | |
238 | err := d.parseReference(documentNode, currentSchema) | |
239 | ||
159 | 240 | if err != nil { |
160 | 241 | return err |
161 | 242 | } |
162 | 243 | |
163 | currentSchema.ref = inheritedReference | |
164 | } | |
165 | ||
166 | if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok { | |
167 | currentSchema.refSchema = sch | |
168 | ||
169 | } else { | |
170 | err := d.parseReference(documentNode, currentSchema, k) | |
171 | if err != nil { | |
172 | return err | |
173 | } | |
174 | ||
175 | 244 | return nil |
176 | 245 | } |
177 | } | |
178 | ||
179 | // definitions | |
180 | if existsMapKey(m, KEY_DEFINITIONS) { | |
181 | if isKind(m[KEY_DEFINITIONS], reflect.Map) { | |
182 | currentSchema.definitions = make(map[string]*subSchema) | |
183 | for dk, dv := range m[KEY_DEFINITIONS].(map[string]interface{}) { | |
184 | if isKind(dv, reflect.Map) { | |
185 | newSchema := &subSchema{property: KEY_DEFINITIONS, parent: currentSchema, ref: currentSchema.ref} | |
186 | currentSchema.definitions[dk] = newSchema | |
187 | err := d.parseSchema(dv, newSchema) | |
188 | if err != nil { | |
189 | return errors.New(err.Error()) | |
190 | } | |
191 | } else { | |
192 | return errors.New(formatErrorDescription( | |
193 | Locale.InvalidType(), | |
194 | ErrorDetails{ | |
195 | "expected": STRING_ARRAY_OF_SCHEMAS, | |
196 | "given": KEY_DEFINITIONS, | |
197 | }, | |
198 | )) | |
199 | } | |
200 | } | |
201 | } else { | |
202 | return errors.New(formatErrorDescription( | |
203 | Locale.InvalidType(), | |
204 | ErrorDetails{ | |
205 | "expected": STRING_ARRAY_OF_SCHEMAS, | |
206 | "given": KEY_DEFINITIONS, | |
207 | }, | |
208 | )) | |
209 | } | |
210 | ||
211 | } | |
212 | ||
213 | // id | |
214 | if existsMapKey(m, KEY_ID) && !isKind(m[KEY_ID], reflect.String) { | |
215 | return errors.New(formatErrorDescription( | |
216 | Locale.InvalidType(), | |
217 | ErrorDetails{ | |
218 | "expected": TYPE_STRING, | |
219 | "given": KEY_ID, | |
220 | }, | |
221 | )) | |
222 | } | |
223 | if k, ok := m[KEY_ID].(string); ok { | |
224 | currentSchema.id = &k | |
225 | } | |
226 | ||
227 | // title | |
228 | if existsMapKey(m, KEY_TITLE) && !isKind(m[KEY_TITLE], reflect.String) { | |
229 | return errors.New(formatErrorDescription( | |
230 | Locale.InvalidType(), | |
231 | ErrorDetails{ | |
232 | "expected": TYPE_STRING, | |
233 | "given": KEY_TITLE, | |
234 | }, | |
235 | )) | |
236 | } | |
237 | if k, ok := m[KEY_TITLE].(string); ok { | |
238 | currentSchema.title = &k | |
239 | } | |
240 | ||
241 | // description | |
242 | if existsMapKey(m, KEY_DESCRIPTION) && !isKind(m[KEY_DESCRIPTION], reflect.String) { | |
243 | return errors.New(formatErrorDescription( | |
244 | Locale.InvalidType(), | |
245 | ErrorDetails{ | |
246 | "expected": TYPE_STRING, | |
247 | "given": KEY_DESCRIPTION, | |
248 | }, | |
249 | )) | |
250 | } | |
251 | if k, ok := m[KEY_DESCRIPTION].(string); ok { | |
252 | currentSchema.description = &k | |
253 | 246 | } |
254 | 247 | |
255 | 248 | // type |
273 | 266 | "given": KEY_TYPE, |
274 | 267 | }, |
275 | 268 | )) |
276 | } else { | |
277 | currentSchema.types.Add(typeInArray.(string)) | |
269 | } | |
270 | if err := currentSchema.types.Add(typeInArray.(string)); err != nil { | |
271 | return err | |
278 | 272 | } |
279 | 273 | } |
280 | 274 | |
353 | 347 | } |
354 | 348 | } |
355 | 349 | |
350 | // propertyNames | |
351 | if existsMapKey(m, KEY_PROPERTY_NAMES) && *currentSchema.draft >= Draft6 { | |
352 | if isKind(m[KEY_PROPERTY_NAMES], reflect.Map, reflect.Bool) { | |
353 | newSchema := &subSchema{property: KEY_PROPERTY_NAMES, parent: currentSchema, ref: currentSchema.ref} | |
354 | currentSchema.propertyNames = newSchema | |
355 | err := d.parseSchema(m[KEY_PROPERTY_NAMES], newSchema) | |
356 | if err != nil { | |
357 | return err | |
358 | } | |
359 | } else { | |
360 | return errors.New(formatErrorDescription( | |
361 | Locale.InvalidType(), | |
362 | ErrorDetails{ | |
363 | "expected": STRING_SCHEMA, | |
364 | "given": KEY_PATTERN_PROPERTIES, | |
365 | }, | |
366 | )) | |
367 | } | |
368 | } | |
369 | ||
356 | 370 | // dependencies |
357 | 371 | if existsMapKey(m, KEY_DEPENDENCIES) { |
358 | 372 | err := d.parseDependencies(m[KEY_DEPENDENCIES], currentSchema) |
365 | 379 | if existsMapKey(m, KEY_ITEMS) { |
366 | 380 | if isKind(m[KEY_ITEMS], reflect.Slice) { |
367 | 381 | for _, itemElement := range m[KEY_ITEMS].([]interface{}) { |
368 | if isKind(itemElement, reflect.Map) { | |
382 | if isKind(itemElement, reflect.Map, reflect.Bool) { | |
369 | 383 | newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} |
370 | 384 | newSchema.ref = currentSchema.ref |
371 | currentSchema.AddItemsChild(newSchema) | |
385 | currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema) | |
372 | 386 | err := d.parseSchema(itemElement, newSchema) |
373 | 387 | if err != nil { |
374 | 388 | return err |
384 | 398 | } |
385 | 399 | currentSchema.itemsChildrenIsSingleSchema = false |
386 | 400 | } |
387 | } else if isKind(m[KEY_ITEMS], reflect.Map) { | |
401 | } else if isKind(m[KEY_ITEMS], reflect.Map, reflect.Bool) { | |
388 | 402 | newSchema := &subSchema{parent: currentSchema, property: KEY_ITEMS} |
389 | 403 | newSchema.ref = currentSchema.ref |
390 | currentSchema.AddItemsChild(newSchema) | |
404 | currentSchema.itemsChildren = append(currentSchema.itemsChildren, newSchema) | |
391 | 405 | err := d.parseSchema(m[KEY_ITEMS], newSchema) |
392 | 406 | if err != nil { |
393 | 407 | return err |
439 | 453 | }, |
440 | 454 | )) |
441 | 455 | } |
442 | if *multipleOfValue <= 0 { | |
456 | if multipleOfValue.Cmp(big.NewRat(0, 1)) <= 0 { | |
443 | 457 | return errors.New(formatErrorDescription( |
444 | 458 | Locale.GreaterThanZero(), |
445 | 459 | ErrorDetails{"number": KEY_MULTIPLE_OF}, |
460 | 474 | } |
461 | 475 | |
462 | 476 | if existsMapKey(m, KEY_EXCLUSIVE_MINIMUM) { |
463 | if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { | |
477 | switch *currentSchema.draft { | |
478 | case Draft4: | |
479 | if !isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { | |
480 | return errors.New(formatErrorDescription( | |
481 | Locale.InvalidType(), | |
482 | ErrorDetails{ | |
483 | "expected": TYPE_BOOLEAN, | |
484 | "given": KEY_EXCLUSIVE_MINIMUM, | |
485 | }, | |
486 | )) | |
487 | } | |
464 | 488 | if currentSchema.minimum == nil { |
465 | 489 | return errors.New(formatErrorDescription( |
466 | 490 | Locale.CannotBeUsedWithout(), |
467 | 491 | ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, |
468 | 492 | )) |
469 | 493 | } |
470 | exclusiveMinimumValue := m[KEY_EXCLUSIVE_MINIMUM].(bool) | |
471 | currentSchema.exclusiveMinimum = exclusiveMinimumValue | |
472 | } else { | |
473 | return errors.New(formatErrorDescription( | |
474 | Locale.MustBeOfA(), | |
475 | ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": TYPE_BOOLEAN}, | |
476 | )) | |
494 | if m[KEY_EXCLUSIVE_MINIMUM].(bool) { | |
495 | currentSchema.exclusiveMinimum = currentSchema.minimum | |
496 | currentSchema.minimum = nil | |
497 | } | |
498 | case Hybrid: | |
499 | if isKind(m[KEY_EXCLUSIVE_MINIMUM], reflect.Bool) { | |
500 | if currentSchema.minimum == nil { | |
501 | return errors.New(formatErrorDescription( | |
502 | Locale.CannotBeUsedWithout(), | |
503 | ErrorDetails{"x": KEY_EXCLUSIVE_MINIMUM, "y": KEY_MINIMUM}, | |
504 | )) | |
505 | } | |
506 | if m[KEY_EXCLUSIVE_MINIMUM].(bool) { | |
507 | currentSchema.exclusiveMinimum = currentSchema.minimum | |
508 | currentSchema.minimum = nil | |
509 | } | |
510 | } else if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) { | |
511 | currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) | |
512 | } else { | |
513 | return errors.New(formatErrorDescription( | |
514 | Locale.InvalidType(), | |
515 | ErrorDetails{ | |
516 | "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER, | |
517 | "given": KEY_EXCLUSIVE_MINIMUM, | |
518 | }, | |
519 | )) | |
520 | } | |
521 | default: | |
522 | if isJSONNumber(m[KEY_EXCLUSIVE_MINIMUM]) { | |
523 | currentSchema.exclusiveMinimum = mustBeNumber(m[KEY_EXCLUSIVE_MINIMUM]) | |
524 | } else { | |
525 | return errors.New(formatErrorDescription( | |
526 | Locale.InvalidType(), | |
527 | ErrorDetails{ | |
528 | "expected": TYPE_NUMBER, | |
529 | "given": KEY_EXCLUSIVE_MINIMUM, | |
530 | }, | |
531 | )) | |
532 | } | |
477 | 533 | } |
478 | 534 | } |
479 | 535 | |
489 | 545 | } |
490 | 546 | |
491 | 547 | if existsMapKey(m, KEY_EXCLUSIVE_MAXIMUM) { |
492 | if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { | |
548 | switch *currentSchema.draft { | |
549 | case Draft4: | |
550 | if !isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { | |
551 | return errors.New(formatErrorDescription( | |
552 | Locale.InvalidType(), | |
553 | ErrorDetails{ | |
554 | "expected": TYPE_BOOLEAN, | |
555 | "given": KEY_EXCLUSIVE_MAXIMUM, | |
556 | }, | |
557 | )) | |
558 | } | |
493 | 559 | if currentSchema.maximum == nil { |
494 | 560 | return errors.New(formatErrorDescription( |
495 | 561 | Locale.CannotBeUsedWithout(), |
496 | 562 | ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, |
497 | 563 | )) |
498 | 564 | } |
499 | exclusiveMaximumValue := m[KEY_EXCLUSIVE_MAXIMUM].(bool) | |
500 | currentSchema.exclusiveMaximum = exclusiveMaximumValue | |
501 | } else { | |
502 | return errors.New(formatErrorDescription( | |
503 | Locale.MustBeOfA(), | |
504 | ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": STRING_NUMBER}, | |
505 | )) | |
506 | } | |
507 | } | |
508 | ||
509 | if currentSchema.minimum != nil && currentSchema.maximum != nil { | |
510 | if *currentSchema.minimum > *currentSchema.maximum { | |
511 | return errors.New(formatErrorDescription( | |
512 | Locale.CannotBeGT(), | |
513 | ErrorDetails{"x": KEY_MINIMUM, "y": KEY_MAXIMUM}, | |
514 | )) | |
565 | if m[KEY_EXCLUSIVE_MAXIMUM].(bool) { | |
566 | currentSchema.exclusiveMaximum = currentSchema.maximum | |
567 | currentSchema.maximum = nil | |
568 | } | |
569 | case Hybrid: | |
570 | if isKind(m[KEY_EXCLUSIVE_MAXIMUM], reflect.Bool) { | |
571 | if currentSchema.maximum == nil { | |
572 | return errors.New(formatErrorDescription( | |
573 | Locale.CannotBeUsedWithout(), | |
574 | ErrorDetails{"x": KEY_EXCLUSIVE_MAXIMUM, "y": KEY_MAXIMUM}, | |
575 | )) | |
576 | } | |
577 | if m[KEY_EXCLUSIVE_MAXIMUM].(bool) { | |
578 | currentSchema.exclusiveMaximum = currentSchema.maximum | |
579 | currentSchema.maximum = nil | |
580 | } | |
581 | } else if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { | |
582 | currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) | |
583 | } else { | |
584 | return errors.New(formatErrorDescription( | |
585 | Locale.InvalidType(), | |
586 | ErrorDetails{ | |
587 | "expected": TYPE_BOOLEAN + "/" + TYPE_NUMBER, | |
588 | "given": KEY_EXCLUSIVE_MAXIMUM, | |
589 | }, | |
590 | )) | |
591 | } | |
592 | default: | |
593 | if isJSONNumber(m[KEY_EXCLUSIVE_MAXIMUM]) { | |
594 | currentSchema.exclusiveMaximum = mustBeNumber(m[KEY_EXCLUSIVE_MAXIMUM]) | |
595 | } else { | |
596 | return errors.New(formatErrorDescription( | |
597 | Locale.InvalidType(), | |
598 | ErrorDetails{ | |
599 | "expected": TYPE_NUMBER, | |
600 | "given": KEY_EXCLUSIVE_MAXIMUM, | |
601 | }, | |
602 | )) | |
603 | } | |
515 | 604 | } |
516 | 605 | } |
517 | 606 | |
580 | 669 | |
581 | 670 | if existsMapKey(m, KEY_FORMAT) { |
582 | 671 | formatString, ok := m[KEY_FORMAT].(string) |
583 | if ok && FormatCheckers.Has(formatString) { | |
584 | currentSchema.format = formatString | |
585 | } else { | |
586 | return errors.New(formatErrorDescription( | |
587 | Locale.MustBeValidFormat(), | |
588 | ErrorDetails{"key": KEY_FORMAT, "given": m[KEY_FORMAT]}, | |
589 | )) | |
590 | } | |
672 | if !ok { | |
673 | return errors.New(formatErrorDescription( | |
674 | Locale.MustBeOfType(), | |
675 | ErrorDetails{"key": KEY_FORMAT, "type": TYPE_STRING}, | |
676 | )) | |
677 | } | |
678 | currentSchema.format = formatString | |
591 | 679 | } |
592 | 680 | |
593 | 681 | // validation : object |
640 | 728 | requiredValues := m[KEY_REQUIRED].([]interface{}) |
641 | 729 | for _, requiredValue := range requiredValues { |
642 | 730 | if isKind(requiredValue, reflect.String) { |
643 | err := currentSchema.AddRequired(requiredValue.(string)) | |
644 | if err != nil { | |
645 | return err | |
731 | if isStringInSlice(currentSchema.required, requiredValue.(string)) { | |
732 | return errors.New(formatErrorDescription( | |
733 | Locale.KeyItemsMustBeUnique(), | |
734 | ErrorDetails{"key": KEY_REQUIRED}, | |
735 | )) | |
646 | 736 | } |
737 | currentSchema.required = append(currentSchema.required, requiredValue.(string)) | |
647 | 738 | } else { |
648 | 739 | return errors.New(formatErrorDescription( |
649 | 740 | Locale.KeyItemsMustBeOfType(), |
706 | 797 | } |
707 | 798 | } |
708 | 799 | |
800 | if existsMapKey(m, KEY_CONTAINS) && *currentSchema.draft >= Draft6 { | |
801 | newSchema := &subSchema{property: KEY_CONTAINS, parent: currentSchema, ref: currentSchema.ref} | |
802 | currentSchema.contains = newSchema | |
803 | err := d.parseSchema(m[KEY_CONTAINS], newSchema) | |
804 | if err != nil { | |
805 | return err | |
806 | } | |
807 | } | |
808 | ||
709 | 809 | // validation : all |
810 | ||
811 | if existsMapKey(m, KEY_CONST) && *currentSchema.draft >= Draft6 { | |
812 | is, err := marshalWithoutNumber(m[KEY_CONST]) | |
813 | if err != nil { | |
814 | return err | |
815 | } | |
816 | currentSchema._const = is | |
817 | } | |
710 | 818 | |
711 | 819 | if existsMapKey(m, KEY_ENUM) { |
712 | 820 | if isKind(m[KEY_ENUM], reflect.Slice) { |
713 | 821 | for _, v := range m[KEY_ENUM].([]interface{}) { |
714 | err := currentSchema.AddEnum(v) | |
822 | is, err := marshalWithoutNumber(v) | |
715 | 823 | if err != nil { |
716 | 824 | return err |
717 | 825 | } |
826 | if isStringInSlice(currentSchema.enum, *is) { | |
827 | return errors.New(formatErrorDescription( | |
828 | Locale.KeyItemsMustBeUnique(), | |
829 | ErrorDetails{"key": KEY_ENUM}, | |
830 | )) | |
831 | } | |
832 | currentSchema.enum = append(currentSchema.enum, *is) | |
718 | 833 | } |
719 | 834 | } else { |
720 | 835 | return errors.New(formatErrorDescription( |
730 | 845 | if isKind(m[KEY_ONE_OF], reflect.Slice) { |
731 | 846 | for _, v := range m[KEY_ONE_OF].([]interface{}) { |
732 | 847 | newSchema := &subSchema{property: KEY_ONE_OF, parent: currentSchema, ref: currentSchema.ref} |
733 | currentSchema.AddOneOf(newSchema) | |
848 | currentSchema.oneOf = append(currentSchema.oneOf, newSchema) | |
734 | 849 | err := d.parseSchema(v, newSchema) |
735 | 850 | if err != nil { |
736 | 851 | return err |
748 | 863 | if isKind(m[KEY_ANY_OF], reflect.Slice) { |
749 | 864 | for _, v := range m[KEY_ANY_OF].([]interface{}) { |
750 | 865 | newSchema := &subSchema{property: KEY_ANY_OF, parent: currentSchema, ref: currentSchema.ref} |
751 | currentSchema.AddAnyOf(newSchema) | |
866 | currentSchema.anyOf = append(currentSchema.anyOf, newSchema) | |
752 | 867 | err := d.parseSchema(v, newSchema) |
753 | 868 | if err != nil { |
754 | 869 | return err |
766 | 881 | if isKind(m[KEY_ALL_OF], reflect.Slice) { |
767 | 882 | for _, v := range m[KEY_ALL_OF].([]interface{}) { |
768 | 883 | newSchema := &subSchema{property: KEY_ALL_OF, parent: currentSchema, ref: currentSchema.ref} |
769 | currentSchema.AddAllOf(newSchema) | |
884 | currentSchema.allOf = append(currentSchema.allOf, newSchema) | |
770 | 885 | err := d.parseSchema(v, newSchema) |
771 | 886 | if err != nil { |
772 | 887 | return err |
781 | 896 | } |
782 | 897 | |
783 | 898 | if existsMapKey(m, KEY_NOT) { |
784 | if isKind(m[KEY_NOT], reflect.Map) { | |
899 | if isKind(m[KEY_NOT], reflect.Map, reflect.Bool) { | |
785 | 900 | newSchema := &subSchema{property: KEY_NOT, parent: currentSchema, ref: currentSchema.ref} |
786 | currentSchema.SetNot(newSchema) | |
901 | currentSchema.not = newSchema | |
787 | 902 | err := d.parseSchema(m[KEY_NOT], newSchema) |
788 | 903 | if err != nil { |
789 | 904 | return err |
796 | 911 | } |
797 | 912 | } |
798 | 913 | |
914 | if *currentSchema.draft >= Draft7 { | |
915 | if existsMapKey(m, KEY_IF) { | |
916 | if isKind(m[KEY_IF], reflect.Map, reflect.Bool) { | |
917 | newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref} | |
918 | currentSchema._if = newSchema | |
919 | err := d.parseSchema(m[KEY_IF], newSchema) | |
920 | if err != nil { | |
921 | return err | |
922 | } | |
923 | } else { | |
924 | return errors.New(formatErrorDescription( | |
925 | Locale.MustBeOfAn(), | |
926 | ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT}, | |
927 | )) | |
928 | } | |
929 | } | |
930 | ||
931 | if existsMapKey(m, KEY_THEN) { | |
932 | if isKind(m[KEY_THEN], reflect.Map, reflect.Bool) { | |
933 | newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref} | |
934 | currentSchema._then = newSchema | |
935 | err := d.parseSchema(m[KEY_THEN], newSchema) | |
936 | if err != nil { | |
937 | return err | |
938 | } | |
939 | } else { | |
940 | return errors.New(formatErrorDescription( | |
941 | Locale.MustBeOfAn(), | |
942 | ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT}, | |
943 | )) | |
944 | } | |
945 | } | |
946 | ||
947 | if existsMapKey(m, KEY_ELSE) { | |
948 | if isKind(m[KEY_ELSE], reflect.Map, reflect.Bool) { | |
949 | newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref} | |
950 | currentSchema._else = newSchema | |
951 | err := d.parseSchema(m[KEY_ELSE], newSchema) | |
952 | if err != nil { | |
953 | return err | |
954 | } | |
955 | } else { | |
956 | return errors.New(formatErrorDescription( | |
957 | Locale.MustBeOfAn(), | |
958 | ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT}, | |
959 | )) | |
960 | } | |
961 | } | |
962 | } | |
963 | ||
799 | 964 | return nil |
800 | 965 | } |
801 | 966 | |
802 | func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error { | |
803 | var refdDocumentNode interface{} | |
804 | jsonPointer := currentSchema.ref.GetPointer() | |
805 | standaloneDocument := d.pool.GetStandaloneDocument() | |
806 | ||
807 | if standaloneDocument != nil { | |
808 | ||
809 | var err error | |
810 | refdDocumentNode, _, err = jsonPointer.Get(standaloneDocument) | |
811 | if err != nil { | |
812 | return err | |
813 | } | |
814 | ||
815 | } else { | |
816 | dsp, err := d.pool.GetDocument(*currentSchema.ref) | |
817 | if err != nil { | |
818 | return err | |
819 | } | |
820 | ||
821 | refdDocumentNode, _, err = jsonPointer.Get(dsp.Document) | |
822 | if err != nil { | |
823 | return err | |
824 | } | |
825 | ||
826 | } | |
827 | ||
828 | if !isKind(refdDocumentNode, reflect.Map) { | |
967 | func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema) error { | |
968 | var ( | |
969 | refdDocumentNode interface{} | |
970 | dsp *schemaPoolDocument | |
971 | err error | |
972 | ) | |
973 | ||
974 | newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} | |
975 | ||
976 | d.referencePool.Add(currentSchema.ref.String(), newSchema) | |
977 | ||
978 | dsp, err = d.pool.GetDocument(*currentSchema.ref) | |
979 | if err != nil { | |
980 | return err | |
981 | } | |
982 | newSchema.id = currentSchema.ref | |
983 | ||
984 | refdDocumentNode = dsp.Document | |
985 | newSchema.draft = dsp.Draft | |
986 | ||
987 | if err != nil { | |
988 | return err | |
989 | } | |
990 | ||
991 | if !isKind(refdDocumentNode, reflect.Map, reflect.Bool) { | |
829 | 992 | return errors.New(formatErrorDescription( |
830 | 993 | Locale.MustBeOfType(), |
831 | 994 | ErrorDetails{"key": STRING_SCHEMA, "type": TYPE_OBJECT}, |
832 | 995 | )) |
833 | 996 | } |
834 | 997 | |
835 | // returns the loaded referenced subSchema for the caller to update its current subSchema | |
836 | newSchemaDocument := refdDocumentNode.(map[string]interface{}) | |
837 | newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref} | |
838 | d.referencePool.Add(currentSchema.ref.String()+reference, newSchema) | |
839 | ||
840 | err := d.parseSchema(newSchemaDocument, newSchema) | |
998 | err = d.parseSchema(refdDocumentNode, newSchema) | |
841 | 999 | if err != nil { |
842 | 1000 | return err |
843 | 1001 | } |
861 | 1019 | for k := range m { |
862 | 1020 | schemaProperty := k |
863 | 1021 | newSchema := &subSchema{property: schemaProperty, parent: currentSchema, ref: currentSchema.ref} |
864 | currentSchema.AddPropertiesChild(newSchema) | |
1022 | currentSchema.propertiesChildren = append(currentSchema.propertiesChildren, newSchema) | |
865 | 1023 | err := d.parseSchema(m[k], newSchema) |
866 | 1024 | if err != nil { |
867 | 1025 | return err |
899 | 1057 | "type": STRING_SCHEMA_OR_ARRAY_OF_STRINGS, |
900 | 1058 | }, |
901 | 1059 | )) |
902 | } else { | |
903 | valuesToRegister = append(valuesToRegister, value.(string)) | |
904 | } | |
1060 | } | |
1061 | valuesToRegister = append(valuesToRegister, value.(string)) | |
905 | 1062 | currentSchema.dependencies[k] = valuesToRegister |
906 | 1063 | } |
907 | 1064 | |
908 | case reflect.Map: | |
1065 | case reflect.Map, reflect.Bool: | |
909 | 1066 | depSchema := &subSchema{property: k, parent: currentSchema, ref: currentSchema.ref} |
910 | 1067 | err := d.parseSchema(m[k], depSchema) |
911 | 1068 | if err != nil { |
0 | // Copyright 2018 johandorland ( https://github.com/johandorland ) | |
1 | // | |
2 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | // you may not use this file except in compliance with the License. | |
4 | // You may obtain a copy of the License at | |
5 | // | |
6 | // http://www.apache.org/licenses/LICENSE-2.0 | |
7 | // | |
8 | // Unless required by applicable law or agreed to in writing, software | |
9 | // distributed under the License is distributed on an "AS IS" BASIS, | |
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | // See the License for the specific language governing permissions and | |
12 | // limitations under the License. | |
13 | ||
14 | package gojsonschema | |
15 | ||
16 | import ( | |
17 | "bytes" | |
18 | "errors" | |
19 | ||
20 | "github.com/xeipuuv/gojsonreference" | |
21 | ) | |
22 | ||
23 | // SchemaLoader is used to load schemas | |
24 | type SchemaLoader struct { | |
25 | pool *schemaPool | |
26 | AutoDetect bool | |
27 | Validate bool | |
28 | Draft Draft | |
29 | } | |
30 | ||
31 | // NewSchemaLoader creates a new NewSchemaLoader | |
32 | func NewSchemaLoader() *SchemaLoader { | |
33 | ||
34 | ps := &SchemaLoader{ | |
35 | pool: &schemaPool{ | |
36 | schemaPoolDocuments: make(map[string]*schemaPoolDocument), | |
37 | }, | |
38 | AutoDetect: true, | |
39 | Validate: false, | |
40 | Draft: Hybrid, | |
41 | } | |
42 | ps.pool.autoDetect = &ps.AutoDetect | |
43 | ||
44 | return ps | |
45 | } | |
46 | ||
47 | func (sl *SchemaLoader) validateMetaschema(documentNode interface{}) error { | |
48 | ||
49 | var ( | |
50 | schema string | |
51 | err error | |
52 | ) | |
53 | if sl.AutoDetect { | |
54 | schema, _, err = parseSchemaURL(documentNode) | |
55 | if err != nil { | |
56 | return err | |
57 | } | |
58 | } | |
59 | ||
60 | // If no explicit "$schema" is used, use the default metaschema associated with the draft used | |
61 | if schema == "" { | |
62 | if sl.Draft == Hybrid { | |
63 | return nil | |
64 | } | |
65 | schema = drafts.GetSchemaURL(sl.Draft) | |
66 | } | |
67 | ||
68 | //Disable validation when loading the metaschema to prevent an infinite recursive loop | |
69 | sl.Validate = false | |
70 | ||
71 | metaSchema, err := sl.Compile(NewReferenceLoader(schema)) | |
72 | ||
73 | if err != nil { | |
74 | return err | |
75 | } | |
76 | ||
77 | sl.Validate = true | |
78 | ||
79 | result := metaSchema.validateDocument(documentNode) | |
80 | ||
81 | if !result.Valid() { | |
82 | var res bytes.Buffer | |
83 | for _, err := range result.Errors() { | |
84 | res.WriteString(err.String()) | |
85 | res.WriteString("\n") | |
86 | } | |
87 | return errors.New(res.String()) | |
88 | } | |
89 | ||
90 | return nil | |
91 | } | |
92 | ||
93 | // AddSchemas adds an arbritrary amount of schemas to the schema cache. As this function does not require | |
94 | // an explicit URL, every schema should contain an $id, so that it can be referenced by the main schema | |
95 | func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error { | |
96 | emptyRef, _ := gojsonreference.NewJsonReference("") | |
97 | ||
98 | for _, loader := range loaders { | |
99 | doc, err := loader.LoadJSON() | |
100 | ||
101 | if err != nil { | |
102 | return err | |
103 | } | |
104 | ||
105 | if sl.Validate { | |
106 | if err := sl.validateMetaschema(doc); err != nil { | |
107 | return err | |
108 | } | |
109 | } | |
110 | ||
111 | // Directly use the Recursive function, so that it get only added to the schema pool by $id | |
112 | // and not by the ref of the document as it's empty | |
113 | if err = sl.pool.parseReferences(doc, emptyRef, false); err != nil { | |
114 | return err | |
115 | } | |
116 | } | |
117 | ||
118 | return nil | |
119 | } | |
120 | ||
121 | //AddSchema adds a schema under the provided URL to the schema cache | |
122 | func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error { | |
123 | ||
124 | ref, err := gojsonreference.NewJsonReference(url) | |
125 | ||
126 | if err != nil { | |
127 | return err | |
128 | } | |
129 | ||
130 | doc, err := loader.LoadJSON() | |
131 | ||
132 | if err != nil { | |
133 | return err | |
134 | } | |
135 | ||
136 | if sl.Validate { | |
137 | if err := sl.validateMetaschema(doc); err != nil { | |
138 | return err | |
139 | } | |
140 | } | |
141 | ||
142 | return sl.pool.parseReferences(doc, ref, true) | |
143 | } | |
144 | ||
145 | // Compile loads and compiles a schema | |
146 | func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { | |
147 | ||
148 | ref, err := rootSchema.JsonReference() | |
149 | ||
150 | if err != nil { | |
151 | return nil, err | |
152 | } | |
153 | ||
154 | d := Schema{} | |
155 | d.pool = sl.pool | |
156 | d.pool.jsonLoaderFactory = rootSchema.LoaderFactory() | |
157 | d.documentReference = ref | |
158 | d.referencePool = newSchemaReferencePool() | |
159 | ||
160 | var doc interface{} | |
161 | if ref.String() != "" { | |
162 | // Get document from schema pool | |
163 | spd, err := d.pool.GetDocument(d.documentReference) | |
164 | if err != nil { | |
165 | return nil, err | |
166 | } | |
167 | doc = spd.Document | |
168 | } else { | |
169 | // Load JSON directly | |
170 | doc, err = rootSchema.LoadJSON() | |
171 | if err != nil { | |
172 | return nil, err | |
173 | } | |
174 | // References need only be parsed if loading JSON directly | |
175 | // as pool.GetDocument already does this for us if loading by reference | |
176 | err = sl.pool.parseReferences(doc, ref, true) | |
177 | if err != nil { | |
178 | return nil, err | |
179 | } | |
180 | } | |
181 | ||
182 | if sl.Validate { | |
183 | if err := sl.validateMetaschema(doc); err != nil { | |
184 | return nil, err | |
185 | } | |
186 | } | |
187 | ||
188 | draft := sl.Draft | |
189 | if sl.AutoDetect { | |
190 | _, detectedDraft, err := parseSchemaURL(doc) | |
191 | if err != nil { | |
192 | return nil, err | |
193 | } | |
194 | if detectedDraft != nil { | |
195 | draft = *detectedDraft | |
196 | } | |
197 | } | |
198 | ||
199 | err = d.parse(doc, draft) | |
200 | if err != nil { | |
201 | return nil, err | |
202 | } | |
203 | ||
204 | return &d, nil | |
205 | } |
0 | // Copyright 2018 johandorland ( https://github.com/johandorland ) | |
1 | // | |
2 | // Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | // you may not use this file except in compliance with the License. | |
4 | // You may obtain a copy of the License at | |
5 | // | |
6 | // http://www.apache.org/licenses/LICENSE-2.0 | |
7 | // | |
8 | // Unless required by applicable law or agreed to in writing, software | |
9 | // distributed under the License is distributed on an "AS IS" BASIS, | |
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | // See the License for the specific language governing permissions and | |
12 | // limitations under the License. | |
13 | ||
14 | package gojsonschema | |
15 | ||
16 | import ( | |
17 | "github.com/stretchr/testify/require" | |
18 | "testing" | |
19 | ||
20 | "github.com/stretchr/testify/assert" | |
21 | ) | |
22 | ||
23 | func TestSchemaLoaderWithReferenceToAddedSchema(t *testing.T) { | |
24 | sl := NewSchemaLoader() | |
25 | err := sl.AddSchemas(NewStringLoader(`{ | |
26 | "$id" : "http://localhost:1234/test1.json", | |
27 | "type" : "integer" | |
28 | }`)) | |
29 | ||
30 | assert.Nil(t, err) | |
31 | schema, err := sl.Compile(NewReferenceLoader("http://localhost:1234/test1.json")) | |
32 | assert.Nil(t, err) | |
33 | result, err := schema.Validate(NewStringLoader(`"hello"`)) | |
34 | assert.Nil(t, err) | |
35 | if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { | |
36 | t.Errorf("Expected invalid type erorr, instead got %v", result.Errors()) | |
37 | } | |
38 | } | |
39 | ||
40 | func TestCrossReference(t *testing.T) { | |
41 | schema1 := NewStringLoader(`{ | |
42 | "$ref" : "http://localhost:1234/test3.json", | |
43 | "definitions" : { | |
44 | "foo" : { | |
45 | "type" : "integer" | |
46 | } | |
47 | } | |
48 | }`) | |
49 | schema2 := NewStringLoader(`{ | |
50 | "$ref" : "http://localhost:1234/test2.json#/definitions/foo" | |
51 | }`) | |
52 | ||
53 | sl := NewSchemaLoader() | |
54 | err := sl.AddSchema("http://localhost:1234/test2.json", schema1) | |
55 | assert.Nil(t, err) | |
56 | err = sl.AddSchema("http://localhost:1234/test3.json", schema2) | |
57 | assert.Nil(t, err) | |
58 | schema, err := sl.Compile(NewStringLoader(`{"$ref" : "http://localhost:1234/test2.json"}`)) | |
59 | assert.Nil(t, err) | |
60 | result, err := schema.Validate(NewStringLoader(`"hello"`)) | |
61 | assert.Nil(t, err) | |
62 | if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { | |
63 | t.Errorf("Expected invalid type erorr, instead got %v", result.Errors()) | |
64 | } | |
65 | } | |
66 | ||
67 | // Multiple schemas identifying under the same $id should throw an error | |
68 | func TestDoubleIDReference(t *testing.T) { | |
69 | sl := NewSchemaLoader() | |
70 | err := sl.AddSchema("http://localhost:1234/test4.json", NewStringLoader("{}")) | |
71 | assert.Nil(t, err) | |
72 | err = sl.AddSchemas(NewStringLoader(`{ "$id" : "http://localhost:1234/test4.json"}`)) | |
73 | assert.NotNil(t, err) | |
74 | } | |
75 | ||
76 | func TestCustomMetaSchema(t *testing.T) { | |
77 | ||
78 | loader := NewStringLoader(`{ | |
79 | "$id" : "http://localhost:1234/test5.json", | |
80 | "properties" : { | |
81 | "multipleOf" : false | |
82 | } | |
83 | }`) | |
84 | ||
85 | // Test a custom metaschema in which we disallow the use of the keyword "multipleOf" | |
86 | sl := NewSchemaLoader() | |
87 | sl.Validate = true | |
88 | ||
89 | err := sl.AddSchemas(loader) | |
90 | assert.Nil(t, err) | |
91 | _, err = sl.Compile(NewStringLoader(`{ | |
92 | "$id" : "http://localhost:1234/test6.json", | |
93 | "$schema" : "http://localhost:1234/test5.json", | |
94 | "type" : "string" | |
95 | }`)) | |
96 | assert.Nil(t, err) | |
97 | ||
98 | sl = NewSchemaLoader() | |
99 | sl.Validate = true | |
100 | err = sl.AddSchemas(loader) | |
101 | assert.Nil(t, err) | |
102 | _, err = sl.Compile(NewStringLoader(`{ | |
103 | "$id" : "http://localhost:1234/test7.json", | |
104 | "$schema" : "http://localhost:1234/test5.json", | |
105 | "multipleOf" : 5 | |
106 | }`)) | |
107 | assert.NotNil(t, err) | |
108 | } | |
109 | ||
110 | func TestSchemaDetection(t *testing.T) { | |
111 | loader := NewStringLoader(`{ | |
112 | "$schema" : "http://json-schema.org/draft-04/schema#", | |
113 | "exclusiveMinimum" : 5 | |
114 | }`) | |
115 | ||
116 | // The schema should produce an error in draft-04 mode | |
117 | _, err := NewSchema(loader) | |
118 | assert.NotNil(t, err) | |
119 | ||
120 | // With schema detection disabled the schema should not produce an error in hybrid mode | |
121 | sl := NewSchemaLoader() | |
122 | sl.AutoDetect = false | |
123 | ||
124 | _, err = sl.Compile(loader) | |
125 | assert.Nil(t, err) | |
126 | } | |
127 | ||
128 | func TestDraftCrossReferencing(t *testing.T) { | |
129 | ||
130 | // Tests the following cross referencing with any combination | |
131 | // of autodetection and preset draft version. | |
132 | ||
133 | loader1 := NewStringLoader(`{ | |
134 | "$schema" : "http://json-schema.org/draft-04/schema#", | |
135 | "id" : "http://localhost:1234/file.json", | |
136 | "$id" : "http://localhost:1234/file.json", | |
137 | "exclusiveMinimum" : 5 | |
138 | }`) | |
139 | loader2 := NewStringLoader(`{ | |
140 | "$schema" : "http://json-schema.org/draft-07/schema#", | |
141 | "id" : "http://localhost:1234/main.json", | |
142 | "$id" : "http://localhost:1234/main.json", | |
143 | "$ref" : "file.json" | |
144 | }`) | |
145 | ||
146 | for _, b := range []bool{true, false} { | |
147 | for _, draft := range []Draft{Draft4, Draft6, Draft7} { | |
148 | sl := NewSchemaLoader() | |
149 | sl.Draft = draft | |
150 | sl.AutoDetect = b | |
151 | ||
152 | err := sl.AddSchemas(loader1) | |
153 | assert.Nil(t, err) | |
154 | _, err = sl.Compile(loader2) | |
155 | ||
156 | // It will always fail with autodetection on as "exclusiveMinimum" : 5 | |
157 | // is only valid since draft-06. With autodetection off it will pass if | |
158 | // draft-06 or newer is used | |
159 | ||
160 | assert.Equal(t, err == nil, !b && draft >= Draft6) | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | const not_map_interface = "not map interface" | |
166 | ||
167 | func TestParseSchemaURL_NotMap(t *testing.T) { | |
168 | //GIVEN | |
169 | sl := NewGoLoader(not_map_interface) | |
170 | //WHEN | |
171 | _, err := NewSchema(sl) | |
172 | //THEN | |
173 | require.Error(t, err) | |
174 | assert.EqualError(t, err, "schema is invalid") | |
175 | } |
27 | 27 | |
28 | 28 | import ( |
29 | 29 | "errors" |
30 | "fmt" | |
31 | "reflect" | |
30 | 32 | |
31 | 33 | "github.com/xeipuuv/gojsonreference" |
32 | 34 | ) |
33 | 35 | |
34 | 36 | type schemaPoolDocument struct { |
35 | 37 | Document interface{} |
38 | Draft *Draft | |
36 | 39 | } |
37 | 40 | |
38 | 41 | type schemaPool struct { |
39 | 42 | schemaPoolDocuments map[string]*schemaPoolDocument |
40 | standaloneDocument interface{} | |
41 | 43 | jsonLoaderFactory JSONLoaderFactory |
42 | } | |
43 | ||
44 | func newSchemaPool(f JSONLoaderFactory) *schemaPool { | |
45 | ||
46 | p := &schemaPool{} | |
47 | p.schemaPoolDocuments = make(map[string]*schemaPoolDocument) | |
48 | p.standaloneDocument = nil | |
49 | p.jsonLoaderFactory = f | |
50 | ||
51 | return p | |
52 | } | |
53 | ||
54 | func (p *schemaPool) SetStandaloneDocument(document interface{}) { | |
55 | p.standaloneDocument = document | |
56 | } | |
57 | ||
58 | func (p *schemaPool) GetStandaloneDocument() (document interface{}) { | |
59 | return p.standaloneDocument | |
44 | autoDetect *bool | |
45 | } | |
46 | ||
47 | func (p *schemaPool) parseReferences(document interface{}, ref gojsonreference.JsonReference, pooled bool) error { | |
48 | ||
49 | var ( | |
50 | draft *Draft | |
51 | err error | |
52 | reference = ref.String() | |
53 | ) | |
54 | // Only the root document should be added to the schema pool if pooled is true | |
55 | if _, ok := p.schemaPoolDocuments[reference]; pooled && ok { | |
56 | return fmt.Errorf("Reference already exists: \"%s\"", reference) | |
57 | } | |
58 | ||
59 | if *p.autoDetect { | |
60 | _, draft, err = parseSchemaURL(document) | |
61 | if err != nil { | |
62 | return err | |
63 | } | |
64 | } | |
65 | ||
66 | err = p.parseReferencesRecursive(document, ref, draft) | |
67 | ||
68 | if pooled { | |
69 | p.schemaPoolDocuments[reference] = &schemaPoolDocument{Document: document, Draft: draft} | |
70 | } | |
71 | ||
72 | return err | |
73 | } | |
74 | ||
75 | func (p *schemaPool) parseReferencesRecursive(document interface{}, ref gojsonreference.JsonReference, draft *Draft) error { | |
76 | // parseReferencesRecursive parses a JSON document and resolves all $id and $ref references. | |
77 | // For $ref references it takes into account the $id scope it is in and replaces | |
78 | // the reference by the absolute resolved reference | |
79 | ||
80 | // When encountering errors it fails silently. Error handling is done when the schema | |
81 | // is syntactically parsed and any error encountered here should also come up there. | |
82 | switch m := document.(type) { | |
83 | case []interface{}: | |
84 | for _, v := range m { | |
85 | p.parseReferencesRecursive(v, ref, draft) | |
86 | } | |
87 | case map[string]interface{}: | |
88 | localRef := &ref | |
89 | ||
90 | keyID := KEY_ID_NEW | |
91 | if existsMapKey(m, KEY_ID) { | |
92 | keyID = KEY_ID | |
93 | } | |
94 | if existsMapKey(m, keyID) && isKind(m[keyID], reflect.String) { | |
95 | jsonReference, err := gojsonreference.NewJsonReference(m[keyID].(string)) | |
96 | if err == nil { | |
97 | localRef, err = ref.Inherits(jsonReference) | |
98 | if err == nil { | |
99 | if _, ok := p.schemaPoolDocuments[localRef.String()]; ok { | |
100 | return fmt.Errorf("Reference already exists: \"%s\"", localRef.String()) | |
101 | } | |
102 | p.schemaPoolDocuments[localRef.String()] = &schemaPoolDocument{Document: document, Draft: draft} | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | if existsMapKey(m, KEY_REF) && isKind(m[KEY_REF], reflect.String) { | |
108 | jsonReference, err := gojsonreference.NewJsonReference(m[KEY_REF].(string)) | |
109 | if err == nil { | |
110 | absoluteRef, err := localRef.Inherits(jsonReference) | |
111 | if err == nil { | |
112 | m[KEY_REF] = absoluteRef.String() | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | for k, v := range m { | |
118 | // const and enums should be interpreted literally, so ignore them | |
119 | if k == KEY_CONST || k == KEY_ENUM { | |
120 | continue | |
121 | } | |
122 | // Something like a property or a dependency is not a valid schema, as it might describe properties named "$ref", "$id" or "const", etc | |
123 | // Therefore don't treat it like a schema. | |
124 | if k == KEY_PROPERTIES || k == KEY_DEPENDENCIES || k == KEY_PATTERN_PROPERTIES { | |
125 | if child, ok := v.(map[string]interface{}); ok { | |
126 | for _, v := range child { | |
127 | p.parseReferencesRecursive(v, *localRef, draft) | |
128 | } | |
129 | } | |
130 | } else { | |
131 | p.parseReferencesRecursive(v, *localRef, draft) | |
132 | } | |
133 | } | |
134 | } | |
135 | return nil | |
60 | 136 | } |
61 | 137 | |
62 | 138 | func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*schemaPoolDocument, error) { |
139 | ||
140 | var ( | |
141 | spd *schemaPoolDocument | |
142 | draft *Draft | |
143 | ok bool | |
144 | err error | |
145 | ) | |
63 | 146 | |
64 | 147 | if internalLogEnabled { |
65 | 148 | internalLog("Get Document ( %s )", reference.String()) |
66 | 149 | } |
67 | 150 | |
68 | var err error | |
69 | ||
70 | // It is not possible to load anything that is not canonical... | |
151 | // Create a deep copy, so we can remove the fragment part later on without altering the original | |
152 | refToURL, _ := gojsonreference.NewJsonReference(reference.String()) | |
153 | ||
154 | // First check if the given fragment is a location independent identifier | |
155 | // http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3 | |
156 | ||
157 | if spd, ok = p.schemaPoolDocuments[refToURL.String()]; ok { | |
158 | if internalLogEnabled { | |
159 | internalLog(" From pool") | |
160 | } | |
161 | return spd, nil | |
162 | } | |
163 | ||
164 | // If the given reference is not a location independent identifier, | |
165 | // strip the fragment and look for a document with it's base URI | |
166 | ||
167 | refToURL.GetUrl().Fragment = "" | |
168 | ||
169 | if cachedSpd, ok := p.schemaPoolDocuments[refToURL.String()]; ok { | |
170 | document, _, err := reference.GetPointer().Get(cachedSpd.Document) | |
171 | ||
172 | if err != nil { | |
173 | return nil, err | |
174 | } | |
175 | ||
176 | if internalLogEnabled { | |
177 | internalLog(" From pool") | |
178 | } | |
179 | ||
180 | spd = &schemaPoolDocument{Document: document, Draft: cachedSpd.Draft} | |
181 | p.schemaPoolDocuments[reference.String()] = spd | |
182 | ||
183 | return spd, nil | |
184 | } | |
185 | ||
186 | // It is not possible to load anything remotely that is not canonical... | |
71 | 187 | if !reference.IsCanonical() { |
72 | 188 | return nil, errors.New(formatErrorDescription( |
73 | 189 | Locale.ReferenceMustBeCanonical(), |
74 | ErrorDetails{"reference": reference}, | |
190 | ErrorDetails{"reference": reference.String()}, | |
75 | 191 | )) |
76 | } | |
77 | ||
78 | refToUrl := reference | |
79 | refToUrl.GetUrl().Fragment = "" | |
80 | ||
81 | var spd *schemaPoolDocument | |
82 | ||
83 | // Try to find the requested document in the pool | |
84 | for k := range p.schemaPoolDocuments { | |
85 | if k == refToUrl.String() { | |
86 | spd = p.schemaPoolDocuments[k] | |
87 | } | |
88 | } | |
89 | ||
90 | if spd != nil { | |
91 | if internalLogEnabled { | |
92 | internalLog(" From pool") | |
93 | } | |
94 | return spd, nil | |
95 | 192 | } |
96 | 193 | |
97 | 194 | jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String()) |
98 | 195 | document, err := jsonReferenceLoader.LoadJSON() |
196 | ||
99 | 197 | if err != nil { |
100 | 198 | return nil, err |
101 | 199 | } |
102 | 200 | |
103 | spd = &schemaPoolDocument{Document: document} | |
104 | // add the document to the pool for potential later use | |
105 | p.schemaPoolDocuments[refToUrl.String()] = spd | |
106 | ||
107 | return spd, nil | |
108 | } | |
201 | // add the whole document to the pool for potential re-use | |
202 | p.parseReferences(document, refToURL, true) | |
203 | ||
204 | _, draft, _ = parseSchemaURL(document) | |
205 | ||
206 | // resolve the potential fragment and also cache it | |
207 | document, _, err = reference.GetPointer().Get(document) | |
208 | ||
209 | if err != nil { | |
210 | return nil, err | |
211 | } | |
212 | ||
213 | return &schemaPoolDocument{Document: document, Draft: draft}, nil | |
214 | } |
61 | 61 | if internalLogEnabled { |
62 | 62 | internalLog(fmt.Sprintf("Add Schema Reference %s to pool", ref)) |
63 | 63 | } |
64 | ||
65 | p.documents[ref] = sch | |
64 | if _, ok := p.documents[ref]; !ok { | |
65 | p.documents[ref] = sch | |
66 | } | |
66 | 67 | } |
43 | 43 | func (t *jsonSchemaType) Add(etype string) error { |
44 | 44 | |
45 | 45 | if !isStringInSlice(JSON_TYPES, etype) { |
46 | return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"type": etype})) | |
46 | return errors.New(formatErrorDescription(Locale.NotAValidType(), ErrorDetails{"given": "/" + etype + "/", "expected": JSON_TYPES})) | |
47 | 47 | } |
48 | 48 | |
49 | 49 | if t.Contains(etype) { |
29 | 29 | "fmt" |
30 | 30 | "io" |
31 | 31 | "io/ioutil" |
32 | "net/http" | |
33 | 32 | "os" |
34 | "reflect" | |
35 | "regexp" | |
36 | "sort" | |
37 | "strconv" | |
38 | "strings" | |
33 | "path/filepath" | |
39 | 34 | "testing" |
40 | 35 | |
41 | 36 | "github.com/stretchr/testify/assert" |
42 | 37 | ) |
43 | 38 | |
44 | 39 | const displayErrorMessages = false |
45 | ||
46 | var rxInvoice = regexp.MustCompile("^[A-Z]{2}-[0-9]{5}") | |
47 | var rxSplitErrors = regexp.MustCompile(", ?") | |
48 | ||
49 | // Used for remote schema in ref/schema_5.json that defines "uri" and "regex" types | |
50 | type alwaysTrueFormatChecker struct{} | |
51 | type invoiceFormatChecker struct{} | |
52 | ||
53 | func (a alwaysTrueFormatChecker) IsFormat(input string) bool { | |
54 | return true | |
55 | } | |
56 | ||
57 | func (a invoiceFormatChecker) IsFormat(input string) bool { | |
58 | return rxInvoice.MatchString(input) | |
59 | } | |
60 | ||
61 | func TestJsonSchemaTestSuite(t *testing.T) { | |
62 | ||
63 | JsonSchemaTestSuiteMap := []map[string]string{ | |
64 | ||
65 | {"phase": "integer type matches integers", "test": "an integer is an integer", "schema": "type/schema_0.json", "data": "type/data_00.json", "valid": "true"}, | |
66 | {"phase": "integer type matches integers", "test": "a float is not an integer", "schema": "type/schema_0.json", "data": "type/data_01.json", "valid": "false", "errors": "invalid_type"}, | |
67 | {"phase": "integer type matches integers", "test": "a string is not an integer", "schema": "type/schema_0.json", "data": "type/data_02.json", "valid": "false", "errors": "invalid_type"}, | |
68 | {"phase": "integer type matches integers", "test": "an object is not an integer", "schema": "type/schema_0.json", "data": "type/data_03.json", "valid": "false", "errors": "invalid_type"}, | |
69 | {"phase": "integer type matches integers", "test": "an array is not an integer", "schema": "type/schema_0.json", "data": "type/data_04.json", "valid": "false", "errors": "invalid_type"}, | |
70 | {"phase": "integer type matches integers", "test": "a boolean is not an integer", "schema": "type/schema_0.json", "data": "type/data_05.json", "valid": "false", "errors": "invalid_type"}, | |
71 | {"phase": "integer type matches integers", "test": "null is not an integer", "schema": "type/schema_0.json", "data": "type/data_06.json", "valid": "false", "errors": "invalid_type"}, | |
72 | {"phase": "number type matches numbers", "test": "an integer is a number", "schema": "type/schema_1.json", "data": "type/data_10.json", "valid": "true"}, | |
73 | {"phase": "number type matches numbers", "test": "a float is a number", "schema": "type/schema_1.json", "data": "type/data_11.json", "valid": "true"}, | |
74 | {"phase": "number type matches numbers", "test": "a string is not a number", "schema": "type/schema_1.json", "data": "type/data_12.json", "valid": "false", "errors": "invalid_type"}, | |
75 | {"phase": "number type matches numbers", "test": "an object is not a number", "schema": "type/schema_1.json", "data": "type/data_13.json", "valid": "false", "errors": "invalid_type"}, | |
76 | {"phase": "number type matches numbers", "test": "an array is not a number", "schema": "type/schema_1.json", "data": "type/data_14.json", "valid": "false", "errors": "invalid_type"}, | |
77 | {"phase": "number type matches numbers", "test": "a boolean is not a number", "schema": "type/schema_1.json", "data": "type/data_15.json", "valid": "false", "errors": "invalid_type"}, | |
78 | {"phase": "number type matches numbers", "test": "null is not a number", "schema": "type/schema_1.json", "data": "type/data_16.json", "valid": "false", "errors": "invalid_type"}, | |
79 | {"phase": "string type matches strings", "test": "1 is not a string", "schema": "type/schema_2.json", "data": "type/data_20.json", "valid": "false", "errors": "invalid_type"}, | |
80 | {"phase": "string type matches strings", "test": "a float is not a string", "schema": "type/schema_2.json", "data": "type/data_21.json", "valid": "false", "errors": "invalid_type"}, | |
81 | {"phase": "string type matches strings", "test": "a string is a string", "schema": "type/schema_2.json", "data": "type/data_22.json", "valid": "true"}, | |
82 | {"phase": "string type matches strings", "test": "an object is not a string", "schema": "type/schema_2.json", "data": "type/data_23.json", "valid": "false", "errors": "invalid_type"}, | |
83 | {"phase": "string type matches strings", "test": "an array is not a string", "schema": "type/schema_2.json", "data": "type/data_24.json", "valid": "false", "errors": "invalid_type"}, | |
84 | {"phase": "string type matches strings", "test": "a boolean is not a string", "schema": "type/schema_2.json", "data": "type/data_25.json", "valid": "false", "errors": "invalid_type"}, | |
85 | {"phase": "string type matches strings", "test": "null is not a string", "schema": "type/schema_2.json", "data": "type/data_26.json", "valid": "false", "errors": "invalid_type"}, | |
86 | {"phase": "object type matches objects", "test": "an integer is not an object", "schema": "type/schema_3.json", "data": "type/data_30.json", "valid": "false", "errors": "invalid_type"}, | |
87 | {"phase": "object type matches objects", "test": "a float is not an object", "schema": "type/schema_3.json", "data": "type/data_31.json", "valid": "false", "errors": "invalid_type"}, | |
88 | {"phase": "object type matches objects", "test": "a string is not an object", "schema": "type/schema_3.json", "data": "type/data_32.json", "valid": "false", "errors": "invalid_type"}, | |
89 | {"phase": "object type matches objects", "test": "an object is an object", "schema": "type/schema_3.json", "data": "type/data_33.json", "valid": "true"}, | |
90 | {"phase": "object type matches objects", "test": "an array is not an object", "schema": "type/schema_3.json", "data": "type/data_34.json", "valid": "false", "errors": "invalid_type"}, | |
91 | {"phase": "object type matches objects", "test": "a boolean is not an object", "schema": "type/schema_3.json", "data": "type/data_35.json", "valid": "false", "errors": "invalid_type"}, | |
92 | {"phase": "object type matches objects", "test": "null is not an object", "schema": "type/schema_3.json", "data": "type/data_36.json", "valid": "false", "errors": "invalid_type"}, | |
93 | {"phase": "array type matches arrays", "test": "an integer is not an array", "schema": "type/schema_4.json", "data": "type/data_40.json", "valid": "false", "errors": "invalid_type"}, | |
94 | {"phase": "array type matches arrays", "test": "a float is not an array", "schema": "type/schema_4.json", "data": "type/data_41.json", "valid": "false", "errors": "invalid_type"}, | |
95 | {"phase": "array type matches arrays", "test": "a string is not an array", "schema": "type/schema_4.json", "data": "type/data_42.json", "valid": "false", "errors": "invalid_type"}, | |
96 | {"phase": "array type matches arrays", "test": "an object is not an array", "schema": "type/schema_4.json", "data": "type/data_43.json", "valid": "false", "errors": "invalid_type"}, | |
97 | {"phase": "array type matches arrays", "test": "an array is not an array", "schema": "type/schema_4.json", "data": "type/data_44.json", "valid": "true"}, | |
98 | {"phase": "array type matches arrays", "test": "a boolean is not an array", "schema": "type/schema_4.json", "data": "type/data_45.json", "valid": "false", "errors": "invalid_type"}, | |
99 | {"phase": "array type matches arrays", "test": "null is not an array", "schema": "type/schema_4.json", "data": "type/data_46.json", "valid": "false", "errors": "invalid_type"}, | |
100 | {"phase": "boolean type matches booleans", "test": "an integer is not a boolean", "schema": "type/schema_5.json", "data": "type/data_50.json", "valid": "false", "errors": "invalid_type"}, | |
101 | {"phase": "boolean type matches booleans", "test": "a float is not a boolean", "schema": "type/schema_5.json", "data": "type/data_51.json", "valid": "false", "errors": "invalid_type"}, | |
102 | {"phase": "boolean type matches booleans", "test": "a string is not a boolean", "schema": "type/schema_5.json", "data": "type/data_52.json", "valid": "false", "errors": "invalid_type"}, | |
103 | {"phase": "boolean type matches booleans", "test": "an object is not a boolean", "schema": "type/schema_5.json", "data": "type/data_53.json", "valid": "false", "errors": "invalid_type"}, | |
104 | {"phase": "boolean type matches booleans", "test": "an array is not a boolean", "schema": "type/schema_5.json", "data": "type/data_54.json", "valid": "false", "errors": "invalid_type"}, | |
105 | {"phase": "boolean type matches booleans", "test": "a boolean is not a boolean", "schema": "type/schema_5.json", "data": "type/data_55.json", "valid": "true", "errors": "invalid_type"}, | |
106 | {"phase": "boolean type matches booleans", "test": "null is not a boolean", "schema": "type/schema_5.json", "data": "type/data_56.json", "valid": "false", "errors": "invalid_type"}, | |
107 | {"phase": "null type matches only the null object", "test": "an integer is not null", "schema": "type/schema_6.json", "data": "type/data_60.json", "valid": "false", "errors": "invalid_type"}, | |
108 | {"phase": "null type matches only the null object", "test": "a float is not null", "schema": "type/schema_6.json", "data": "type/data_61.json", "valid": "false", "errors": "invalid_type"}, | |
109 | {"phase": "null type matches only the null object", "test": "a string is not null", "schema": "type/schema_6.json", "data": "type/data_62.json", "valid": "false", "errors": "invalid_type"}, | |
110 | {"phase": "null type matches only the null object", "test": "an object is not null", "schema": "type/schema_6.json", "data": "type/data_63.json", "valid": "false", "errors": "invalid_type"}, | |
111 | {"phase": "null type matches only the null object", "test": "an array is not null", "schema": "type/schema_6.json", "data": "type/data_64.json", "valid": "false", "errors": "invalid_type"}, | |
112 | {"phase": "null type matches only the null object", "test": "a boolean is not null", "schema": "type/schema_6.json", "data": "type/data_65.json", "valid": "false", "errors": "invalid_type"}, | |
113 | {"phase": "null type matches only the null object", "test": "null is null", "schema": "type/schema_6.json", "data": "type/data_66.json", "valid": "true"}, | |
114 | {"phase": "multiple types can be specified in an array", "test": "an integer is valid", "schema": "type/schema_7.json", "data": "type/data_70.json", "valid": "true"}, | |
115 | {"phase": "multiple types can be specified in an array", "test": "a string is valid", "schema": "type/schema_7.json", "data": "type/data_71.json", "valid": "true"}, | |
116 | {"phase": "multiple types can be specified in an array", "test": "a float is invalid", "schema": "type/schema_7.json", "data": "type/data_72.json", "valid": "false", "errors": "invalid_type"}, | |
117 | {"phase": "multiple types can be specified in an array", "test": "an object is invalid", "schema": "type/schema_7.json", "data": "type/data_73.json", "valid": "false", "errors": "invalid_type"}, | |
118 | {"phase": "multiple types can be specified in an array", "test": "an array is invalid", "schema": "type/schema_7.json", "data": "type/data_74.json", "valid": "false", "errors": "invalid_type"}, | |
119 | {"phase": "multiple types can be specified in an array", "test": "a boolean is invalid", "schema": "type/schema_7.json", "data": "type/data_75.json", "valid": "false", "errors": "invalid_type"}, | |
120 | {"phase": "multiple types can be specified in an array", "test": "null is invalid", "schema": "type/schema_7.json", "data": "type/data_76.json", "valid": "false", "errors": "invalid_type"}, | |
121 | {"phase": "required validation", "test": "present required property is valid", "schema": "required/schema_0.json", "data": "required/data_00.json", "valid": "true"}, | |
122 | {"phase": "required validation", "test": "non-present required property is invalid", "schema": "required/schema_0.json", "data": "required/data_01.json", "valid": "false", "errors": "required"}, | |
123 | {"phase": "required default validation", "test": "not required by default", "schema": "required/schema_1.json", "data": "required/data_10.json", "valid": "true"}, | |
124 | {"phase": "uniqueItems validation", "test": "unique array of integers is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_00.json", "valid": "true"}, | |
125 | {"phase": "uniqueItems validation", "test": "non-unique array of integers is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_01.json", "valid": "false", "errors": "unique"}, | |
126 | {"phase": "uniqueItems validation", "test": "numbers are unique if mathematically unequal", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_02.json", "valid": "false", "errors": "unique, unique"}, | |
127 | {"phase": "uniqueItems validation", "test": "unique array of objects is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_03.json", "valid": "true"}, | |
128 | {"phase": "uniqueItems validation", "test": "non-unique array of objects is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_04.json", "valid": "false", "errors": "unique"}, | |
129 | {"phase": "uniqueItems validation", "test": "unique array of nested objects is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_05.json", "valid": "true"}, | |
130 | {"phase": "uniqueItems validation", "test": "non-unique array of nested objects is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_06.json", "valid": "false", "errors": "unique"}, | |
131 | {"phase": "uniqueItems validation", "test": "unique array of arrays is valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_07.json", "valid": "true"}, | |
132 | {"phase": "uniqueItems validation", "test": "non-unique array of arrays is invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_08.json", "valid": "false", "errors": "unique"}, | |
133 | {"phase": "uniqueItems validation", "test": "1 and true are unique", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_09.json", "valid": "true"}, | |
134 | {"phase": "uniqueItems validation", "test": "0 and false are unique", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_010.json", "valid": "true"}, | |
135 | {"phase": "uniqueItems validation", "test": "unique heterogeneous types are valid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_011.json", "valid": "true"}, | |
136 | {"phase": "uniqueItems validation", "test": "non-unique heterogeneous types are invalid", "schema": "uniqueItems/schema_0.json", "data": "uniqueItems/data_012.json", "valid": "false", "errors": "unique"}, | |
137 | {"phase": "pattern validation", "test": "a matching pattern is valid", "schema": "pattern/schema_0.json", "data": "pattern/data_00.json", "valid": "true"}, | |
138 | {"phase": "pattern validation", "test": "a non-matching pattern is invalid", "schema": "pattern/schema_0.json", "data": "pattern/data_01.json", "valid": "false", "errors": "pattern"}, | |
139 | {"phase": "pattern validation", "test": "ignores non-strings", "schema": "pattern/schema_0.json", "data": "pattern/data_02.json", "valid": "true"}, | |
140 | {"phase": "simple enum validation", "test": "one of the enum is valid", "schema": "enum/schema_0.json", "data": "enum/data_00.json", "valid": "true"}, | |
141 | {"phase": "simple enum validation", "test": "something else is invalid", "schema": "enum/schema_0.json", "data": "enum/data_01.json", "valid": "false", "errors": "enum"}, | |
142 | {"phase": "heterogeneous enum validation", "test": "one of the enum is valid", "schema": "enum/schema_1.json", "data": "enum/data_10.json", "valid": "true"}, | |
143 | {"phase": "heterogeneous enum validation", "test": "something else is invalid", "schema": "enum/schema_1.json", "data": "enum/data_11.json", "valid": "false", "errors": "enum"}, | |
144 | {"phase": "heterogeneous enum validation", "test": "objects are deep compared", "schema": "enum/schema_1.json", "data": "enum/data_12.json", "valid": "false", "errors": "enum"}, | |
145 | {"phase": "minLength validation", "test": "longer is valid", "schema": "minLength/schema_0.json", "data": "minLength/data_00.json", "valid": "true"}, | |
146 | {"phase": "minLength validation", "test": "exact length is valid", "schema": "minLength/schema_0.json", "data": "minLength/data_01.json", "valid": "true"}, | |
147 | {"phase": "minLength validation", "test": "too short is invalid", "schema": "minLength/schema_0.json", "data": "minLength/data_02.json", "valid": "false", "errors": "string_gte"}, | |
148 | {"phase": "minLength validation", "test": "ignores non-strings", "schema": "minLength/schema_0.json", "data": "minLength/data_03.json", "valid": "true"}, | |
149 | {"phase": "minLength validation", "test": "counts utf8 length correctly", "schema": "minLength/schema_0.json", "data": "minLength/data_04.json", "valid": "false", "errors": "string_gte"}, | |
150 | {"phase": "maxLength validation", "test": "shorter is valid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_00.json", "valid": "true"}, | |
151 | {"phase": "maxLength validation", "test": "exact length is valid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_01.json", "valid": "true"}, | |
152 | {"phase": "maxLength validation", "test": "too long is invalid", "schema": "maxLength/schema_0.json", "data": "maxLength/data_02.json", "valid": "false", "errors": "string_lte"}, | |
153 | {"phase": "maxLength validation", "test": "ignores non-strings", "schema": "maxLength/schema_0.json", "data": "maxLength/data_03.json", "valid": "true"}, | |
154 | {"phase": "maxLength validation", "test": "counts utf8 length correctly", "schema": "maxLength/schema_0.json", "data": "maxLength/data_04.json", "valid": "true"}, | |
155 | {"phase": "minimum validation", "test": "above the minimum is valid", "schema": "minimum/schema_0.json", "data": "minimum/data_00.json", "valid": "true"}, | |
156 | {"phase": "minimum validation", "test": "below the minimum is invalid", "schema": "minimum/schema_0.json", "data": "minimum/data_01.json", "valid": "false", "errors": "number_gte"}, | |
157 | {"phase": "minimum validation", "test": "ignores non-numbers", "schema": "minimum/schema_0.json", "data": "minimum/data_02.json", "valid": "true"}, | |
158 | {"phase": "exclusiveMinimum validation", "test": "above the minimum is still valid", "schema": "minimum/schema_1.json", "data": "minimum/data_10.json", "valid": "true"}, | |
159 | {"phase": "exclusiveMinimum validation", "test": "boundary point is invalid", "schema": "minimum/schema_1.json", "data": "minimum/data_11.json", "valid": "false", "errors": "number_gt"}, | |
160 | {"phase": "maximum validation", "test": "below the maximum is valid", "schema": "maximum/schema_0.json", "data": "maximum/data_00.json", "valid": "true"}, | |
161 | {"phase": "maximum validation", "test": "above the maximum is invalid", "schema": "maximum/schema_0.json", "data": "maximum/data_01.json", "valid": "false", "errors": "number_lte"}, | |
162 | {"phase": "maximum validation", "test": "ignores non-numbers", "schema": "maximum/schema_0.json", "data": "maximum/data_02.json", "valid": "true"}, | |
163 | {"phase": "exclusiveMaximum validation", "test": "below the maximum is still valid", "schema": "maximum/schema_1.json", "data": "maximum/data_10.json", "valid": "true"}, | |
164 | {"phase": "exclusiveMaximum validation", "test": "boundary point is invalid", "schema": "maximum/schema_1.json", "data": "maximum/data_11.json", "valid": "false", "errors": "number_lt"}, | |
165 | {"phase": "allOf", "test": "allOf", "schema": "allOf/schema_0.json", "data": "allOf/data_00.json", "valid": "true"}, | |
166 | {"phase": "allOf", "test": "mismatch second", "schema": "allOf/schema_0.json", "data": "allOf/data_01.json", "valid": "false", "errors": "number_all_of, required"}, | |
167 | {"phase": "allOf", "test": "mismatch first", "schema": "allOf/schema_0.json", "data": "allOf/data_02.json", "valid": "false", "errors": "number_all_of, required"}, | |
168 | {"phase": "allOf", "test": "wrong type", "schema": "allOf/schema_0.json", "data": "allOf/data_03.json", "valid": "false", "errors": "number_all_of, invalid_type"}, | |
169 | {"phase": "allOf with base schema", "test": "valid", "schema": "allOf/schema_1.json", "data": "allOf/data_10.json", "valid": "true"}, | |
170 | {"phase": "allOf with base schema", "test": "mismatch base schema", "schema": "allOf/schema_1.json", "data": "allOf/data_11.json", "valid": "false", "errors": "required"}, | |
171 | {"phase": "allOf with base schema", "test": "mismatch first allOf", "schema": "allOf/schema_1.json", "data": "allOf/data_12.json", "valid": "false", "errors": "number_all_of, required"}, | |
172 | {"phase": "allOf with base schema", "test": "mismatch second allOf", "schema": "allOf/schema_1.json", "data": "allOf/data_13.json", "valid": "false", "errors": "number_all_of, required"}, | |
173 | {"phase": "allOf with base schema", "test": "mismatch both", "schema": "allOf/schema_1.json", "data": "allOf/data_14.json", "valid": "false", "errors": "number_all_of, required, required"}, | |
174 | {"phase": "allOf simple types", "test": "valid", "schema": "allOf/schema_2.json", "data": "allOf/data_20.json", "valid": "true"}, | |
175 | {"phase": "allOf simple types", "test": "mismatch one", "schema": "allOf/schema_2.json", "data": "allOf/data_21.json", "valid": "false", "errors": "number_all_of, number_lte"}, | |
176 | {"phase": "oneOf", "test": "first oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_00.json", "valid": "true"}, | |
177 | {"phase": "oneOf", "test": "second oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_01.json", "valid": "true"}, | |
178 | {"phase": "oneOf", "test": "both oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_02.json", "valid": "false", "errors": "number_one_of"}, | |
179 | {"phase": "oneOf", "test": "neither oneOf valid", "schema": "oneOf/schema_0.json", "data": "oneOf/data_03.json", "valid": "false"}, | |
180 | {"phase": "oneOf with base schema", "test": "mismatch base schema", "schema": "oneOf/schema_1.json", "data": "oneOf/data_10.json", "valid": "false", "errors": "invalid_type"}, | |
181 | {"phase": "oneOf with base schema", "test": "one oneOf valid", "schema": "oneOf/schema_1.json", "data": "oneOf/data_11.json", "valid": "true"}, | |
182 | {"phase": "oneOf with base schema", "test": "both oneOf valid", "schema": "oneOf/schema_1.json", "data": "oneOf/data_12.json", "valid": "false", "errors": "number_one_of"}, | |
183 | {"phase": "anyOf", "test": "first anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_00.json", "valid": "true"}, | |
184 | {"phase": "anyOf", "test": "second anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_01.json", "valid": "true"}, | |
185 | {"phase": "anyOf", "test": "both anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_02.json", "valid": "true"}, | |
186 | {"phase": "anyOf", "test": "neither anyOf valid", "schema": "anyOf/schema_0.json", "data": "anyOf/data_03.json", "valid": "false", "errors": "number_any_of, number_gte"}, | |
187 | {"phase": "anyOf with base schema", "test": "mismatch base schema", "schema": "anyOf/schema_1.json", "data": "anyOf/data_10.json", "valid": "false", "errors": "invalid_type"}, | |
188 | {"phase": "anyOf with base schema", "test": "one anyOf valid", "schema": "anyOf/schema_1.json", "data": "anyOf/data_11.json", "valid": "true"}, | |
189 | {"phase": "anyOf with base schema", "test": "both anyOf invalid", "schema": "anyOf/schema_1.json", "data": "anyOf/data_12.json", "valid": "false", "errors": "number_any_of, string_lte"}, | |
190 | {"phase": "not", "test": "allowed", "schema": "not/schema_0.json", "data": "not/data_00.json", "valid": "true"}, | |
191 | {"phase": "not", "test": "disallowed", "schema": "not/schema_0.json", "data": "not/data_01.json", "valid": "false", "errors": "number_not"}, | |
192 | {"phase": "not multiple types", "test": "valid", "schema": "not/schema_1.json", "data": "not/data_10.json", "valid": "true"}, | |
193 | {"phase": "not multiple types", "test": "mismatch", "schema": "not/schema_1.json", "data": "not/data_11.json", "valid": "false", "errors": "number_not"}, | |
194 | {"phase": "not multiple types", "test": "other mismatch", "schema": "not/schema_1.json", "data": "not/data_12.json", "valid": "false", "errors": "number_not"}, | |
195 | {"phase": "not more complex schema", "test": "match", "schema": "not/schema_2.json", "data": "not/data_20.json", "valid": "true"}, | |
196 | {"phase": "not more complex schema", "test": "other match", "schema": "not/schema_2.json", "data": "not/data_21.json", "valid": "true"}, | |
197 | {"phase": "not more complex schema", "test": "mismatch", "schema": "not/schema_2.json", "data": "not/data_22.json", "valid": "false", "errors": "number_not"}, | |
198 | {"phase": "minProperties validation", "test": "longer is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_00.json", "valid": "true"}, | |
199 | {"phase": "minProperties validation", "test": "exact length is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_01.json", "valid": "true"}, | |
200 | {"phase": "minProperties validation", "test": "too short is invalid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_02.json", "valid": "false", "errors": "array_min_properties"}, | |
201 | {"phase": "minProperties validation", "test": "ignores non-objects", "schema": "minProperties/schema_0.json", "data": "minProperties/data_03.json", "valid": "true"}, | |
202 | {"phase": "maxProperties validation", "test": "shorter is valid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_00.json", "valid": "true"}, | |
203 | {"phase": "maxProperties validation", "test": "exact length is valid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_01.json", "valid": "true"}, | |
204 | {"phase": "maxProperties validation", "test": "too long is invalid", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_02.json", "valid": "false", "errors": "array_max_properties"}, | |
205 | {"phase": "maxProperties validation", "test": "ignores non-objects", "schema": "maxProperties/schema_0.json", "data": "maxProperties/data_03.json", "valid": "true"}, | |
206 | {"phase": "by int", "test": "int by int", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_00.json", "valid": "true"}, | |
207 | {"phase": "by int", "test": "int by int fail", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_01.json", "valid": "false", "errors": "multiple_of"}, | |
208 | {"phase": "by int", "test": "ignores non-numbers", "schema": "multipleOf/schema_0.json", "data": "multipleOf/data_02.json", "valid": "true"}, | |
209 | {"phase": "by number", "test": "zero is multiple of anything", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_10.json", "valid": "true"}, | |
210 | {"phase": "by number", "test": "4.5 is multiple of 1.5", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_11.json", "valid": "true"}, | |
211 | {"phase": "by number", "test": "35 is not multiple of 1.5", "schema": "multipleOf/schema_1.json", "data": "multipleOf/data_12.json", "valid": "false", "errors": "multiple_of"}, | |
212 | {"phase": "by small number", "test": "0.0075 is multiple of 0.0001", "schema": "multipleOf/schema_2.json", "data": "multipleOf/data_20.json", "valid": "true"}, | |
213 | {"phase": "by small number", "test": "0.00751 is not multiple of 0.0001", "schema": "multipleOf/schema_2.json", "data": "multipleOf/data_21.json", "valid": "false", "errors": "multiple_of"}, | |
214 | {"phase": "minItems validation", "test": "longer is valid", "schema": "minItems/schema_0.json", "data": "minItems/data_00.json", "valid": "true"}, | |
215 | {"phase": "minItems validation", "test": "exact length is valid", "schema": "minItems/schema_0.json", "data": "minItems/data_01.json", "valid": "true"}, | |
216 | {"phase": "minItems validation", "test": "too short is invalid", "schema": "minItems/schema_0.json", "data": "minItems/data_02.json", "valid": "false", "errors": "array_min_items"}, | |
217 | {"phase": "minItems validation", "test": "ignores non-arrays", "schema": "minItems/schema_0.json", "data": "minItems/data_03.json", "valid": "true"}, | |
218 | {"phase": "maxItems validation", "test": "shorter is valid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_00.json", "valid": "true"}, | |
219 | {"phase": "maxItems validation", "test": "exact length is valid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_01.json", "valid": "true"}, | |
220 | {"phase": "maxItems validation", "test": "too long is invalid", "schema": "maxItems/schema_0.json", "data": "maxItems/data_02.json", "valid": "false", "errors": "array_max_items"}, | |
221 | {"phase": "maxItems validation", "test": "ignores non-arrays", "schema": "maxItems/schema_0.json", "data": "maxItems/data_03.json", "valid": "true"}, | |
222 | {"phase": "object properties validation", "test": "both properties present and valid is valid", "schema": "properties/schema_0.json", "data": "properties/data_00.json", "valid": "true"}, | |
223 | {"phase": "object properties validation", "test": "one property invalid is invalid", "schema": "properties/schema_0.json", "data": "properties/data_01.json", "valid": "false", "errors": "invalid_type"}, | |
224 | {"phase": "object properties validation", "test": "both properties invalid is invalid", "schema": "properties/schema_0.json", "data": "properties/data_02.json", "valid": "false", "errors": "invalid_type, invalid_type"}, | |
225 | {"phase": "object properties validation", "test": "doesn't invalidate other properties", "schema": "properties/schema_0.json", "data": "properties/data_03.json", "valid": "true"}, | |
226 | {"phase": "object properties validation", "test": "ignores non-objects", "schema": "properties/schema_0.json", "data": "properties/data_04.json", "valid": "true"}, | |
227 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "property validates property", "schema": "properties/schema_1.json", "data": "properties/data_10.json", "valid": "true"}, | |
228 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "property invalidates property", "schema": "properties/schema_1.json", "data": "properties/data_11.json", "valid": "false", "errors": "array_max_items"}, | |
229 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty invalidates property", "schema": "properties/schema_1.json", "data": "properties/data_12.json", "valid": "false", "errors": "array_min_items, invalid_type"}, | |
230 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty validates nonproperty", "schema": "properties/schema_1.json", "data": "properties/data_13.json", "valid": "true"}, | |
231 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "patternProperty invalidates nonproperty", "schema": "properties/schema_1.json", "data": "properties/data_14.json", "valid": "false", "errors": "array_min_items, invalid_type"}, | |
232 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty ignores property", "schema": "properties/schema_1.json", "data": "properties/data_15.json", "valid": "true"}, | |
233 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty validates others", "schema": "properties/schema_1.json", "data": "properties/data_16.json", "valid": "true"}, | |
234 | {"phase": "properties, patternProperties, additionalProperties interaction", "test": "additionalProperty invalidates others", "schema": "properties/schema_1.json", "data": "properties/data_17.json", "valid": "false", "errors": "invalid_type"}, | |
235 | {"phase": "root pointer ref", "test": "match", "schema": "ref/schema_0.json", "data": "ref/data_00.json", "valid": "true"}, | |
236 | {"phase": "root pointer ref", "test": "recursive match", "schema": "ref/schema_0.json", "data": "ref/data_01.json", "valid": "true"}, | |
237 | {"phase": "root pointer ref", "test": "mismatch", "schema": "ref/schema_0.json", "data": "ref/data_02.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
238 | {"phase": "root pointer ref", "test": "recursive mismatch", "schema": "ref/schema_0.json", "data": "ref/data_03.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
239 | {"phase": "relative pointer ref to object", "test": "match", "schema": "ref/schema_1.json", "data": "ref/data_10.json", "valid": "true"}, | |
240 | {"phase": "relative pointer ref to object", "test": "mismatch", "schema": "ref/schema_1.json", "data": "ref/data_11.json", "valid": "false", "errors": "invalid_type"}, | |
241 | {"phase": "relative pointer ref to array", "test": "match array", "schema": "ref/schema_2.json", "data": "ref/data_20.json", "valid": "true"}, | |
242 | {"phase": "relative pointer ref to array", "test": "mismatch array", "schema": "ref/schema_2.json", "data": "ref/data_21.json", "valid": "false", "errors": "invalid_type"}, | |
243 | {"phase": "escaped pointer ref", "test": "slash", "schema": "ref/schema_3.json", "data": "ref/data_30.json", "valid": "false", "errors": "invalid_type"}, | |
244 | {"phase": "escaped pointer ref", "test": "tilda", "schema": "ref/schema_3.json", "data": "ref/data_31.json", "valid": "false", "errors": "invalid_type"}, | |
245 | {"phase": "escaped pointer ref", "test": "percent", "schema": "ref/schema_3.json", "data": "ref/data_32.json", "valid": "false", "errors": "invalid_type"}, | |
246 | {"phase": "nested refs", "test": "nested ref valid", "schema": "ref/schema_4.json", "data": "ref/data_40.json", "valid": "true"}, | |
247 | {"phase": "nested refs", "test": "nested ref invalid", "schema": "ref/schema_4.json", "data": "ref/data_41.json", "valid": "false", "errors": "invalid_type"}, | |
248 | {"phase": "remote ref, containing refs itself", "test": "remote ref valid", "schema": "ref/schema_5.json", "data": "ref/data_50.json", "valid": "true"}, | |
249 | {"phase": "remote ref, containing refs itself", "test": "remote ref invalid", "schema": "ref/schema_5.json", "data": "ref/data_51.json", "valid": "false", "errors": "number_all_of, number_gte"}, | |
250 | {"phase": "a schema given for items", "test": "valid items", "schema": "items/schema_0.json", "data": "items/data_00.json", "valid": "true"}, | |
251 | {"phase": "a schema given for items", "test": "wrong type of items", "schema": "items/schema_0.json", "data": "items/data_01.json", "valid": "false", "errors": "invalid_type"}, | |
252 | {"phase": "a schema given for items", "test": "ignores non-arrays", "schema": "items/schema_0.json", "data": "items/data_02.json", "valid": "true"}, | |
253 | {"phase": "an array of schemas for items", "test": "correct types", "schema": "items/schema_1.json", "data": "items/data_10.json", "valid": "true"}, | |
254 | {"phase": "an array of schemas for items", "test": "wrong types", "schema": "items/schema_1.json", "data": "items/data_11.json", "valid": "false", "errors": "invalid_type, invalid_type"}, | |
255 | {"phase": "valid definition", "test": "valid definition schema", "schema": "definitions/schema_0.json", "data": "definitions/data_00.json", "valid": "true"}, | |
256 | {"phase": "invalid definition", "test": "invalid definition schema", "schema": "definitions/schema_1.json", "data": "definitions/data_10.json", "valid": "false", "errors": "number_any_of, enum"}, | |
257 | {"phase": "additionalItems as schema", "test": "additional items match schema", "schema": "additionalItems/schema_0.json", "data": "additionalItems/data_00.json", "valid": "true"}, | |
258 | {"phase": "additionalItems as schema", "test": "additional items do not match schema", "schema": "additionalItems/schema_0.json", "data": "additionalItems/data_01.json", "valid": "false", "errors": "invalid_type"}, | |
259 | {"phase": "items is schema, no additionalItems", "test": "all items match schema", "schema": "additionalItems/schema_1.json", "data": "additionalItems/data_10.json", "valid": "true"}, | |
260 | {"phase": "array of items with no additionalItems", "test": "no additional items present", "schema": "additionalItems/schema_2.json", "data": "additionalItems/data_20.json", "valid": "true"}, | |
261 | {"phase": "array of items with no additionalItems", "test": "additional items are not permitted", "schema": "additionalItems/schema_2.json", "data": "additionalItems/data_21.json", "valid": "false", "errors": "array_no_additional_items"}, | |
262 | {"phase": "additionalItems as false without items", "test": "items defaults to empty schema so everything is valid", "schema": "additionalItems/schema_3.json", "data": "additionalItems/data_30.json", "valid": "true"}, | |
263 | {"phase": "additionalItems as false without items", "test": "ignores non-arrays", "schema": "additionalItems/schema_3.json", "data": "additionalItems/data_31.json", "valid": "true"}, | |
264 | {"phase": "additionalItems are allowed by default", "test": "only the first item is validated", "schema": "additionalItems/schema_4.json", "data": "additionalItems/data_40.json", "valid": "true"}, | |
265 | {"phase": "additionalProperties being false does not allow other properties", "test": "no additional properties is valid", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_00.json", "valid": "true"}, | |
266 | {"phase": "additionalProperties being false does not allow other properties", "test": "an additional property is invalid", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_01.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
267 | {"phase": "additionalProperties being false does not allow other properties", "test": "ignores non-objects", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_02.json", "valid": "true"}, | |
268 | {"phase": "additionalProperties being false does not allow other properties", "test": "patternProperties are not additional properties", "schema": "additionalProperties/schema_0.json", "data": "additionalProperties/data_03.json", "valid": "true"}, | |
269 | {"phase": "additionalProperties allows a schema which should validate", "test": "no additional properties is valid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_10.json", "valid": "true"}, | |
270 | {"phase": "additionalProperties allows a schema which should validate", "test": "an additional valid property is valid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_11.json", "valid": "true"}, | |
271 | {"phase": "additionalProperties allows a schema which should validate", "test": "an additional invalid property is invalid", "schema": "additionalProperties/schema_1.json", "data": "additionalProperties/data_12.json", "valid": "false", "errors": "invalid_type"}, | |
272 | {"phase": "additionalProperties are allowed by default", "test": "additional properties are allowed", "schema": "additionalProperties/schema_2.json", "data": "additionalProperties/data_20.json", "valid": "true"}, | |
273 | {"phase": "dependencies", "test": "neither", "schema": "dependencies/schema_0.json", "data": "dependencies/data_00.json", "valid": "true"}, | |
274 | {"phase": "dependencies", "test": "nondependant", "schema": "dependencies/schema_0.json", "data": "dependencies/data_01.json", "valid": "true"}, | |
275 | {"phase": "dependencies", "test": "with dependency", "schema": "dependencies/schema_0.json", "data": "dependencies/data_02.json", "valid": "true"}, | |
276 | {"phase": "dependencies", "test": "missing dependency", "schema": "dependencies/schema_0.json", "data": "dependencies/data_03.json", "valid": "false", "errors": "missing_dependency"}, | |
277 | {"phase": "dependencies", "test": "ignores non-objects", "schema": "dependencies/schema_0.json", "data": "dependencies/data_04.json", "valid": "true"}, | |
278 | {"phase": "multiple dependencies", "test": "neither", "schema": "dependencies/schema_1.json", "data": "dependencies/data_10.json", "valid": "true"}, | |
279 | {"phase": "multiple dependencies", "test": "nondependants", "schema": "dependencies/schema_1.json", "data": "dependencies/data_11.json", "valid": "true"}, | |
280 | {"phase": "multiple dependencies", "test": "with dependencies", "schema": "dependencies/schema_1.json", "data": "dependencies/data_12.json", "valid": "true"}, | |
281 | {"phase": "multiple dependencies", "test": "missing dependency", "schema": "dependencies/schema_1.json", "data": "dependencies/data_13.json", "valid": "false", "errors": "missing_dependency"}, | |
282 | {"phase": "multiple dependencies", "test": "missing other dependency", "schema": "dependencies/schema_1.json", "data": "dependencies/data_14.json", "valid": "false", "errors": "missing_dependency"}, | |
283 | {"phase": "multiple dependencies", "test": "missing both dependencies", "schema": "dependencies/schema_1.json", "data": "dependencies/data_15.json", "valid": "false", "errors": "missing_dependency, missing_dependency"}, | |
284 | {"phase": "multiple dependencies subschema", "test": "valid", "schema": "dependencies/schema_2.json", "data": "dependencies/data_20.json", "valid": "true"}, | |
285 | {"phase": "multiple dependencies subschema", "test": "no dependency", "schema": "dependencies/schema_2.json", "data": "dependencies/data_21.json", "valid": "true"}, | |
286 | {"phase": "multiple dependencies subschema", "test": "wrong type", "schema": "dependencies/schema_2.json", "data": "dependencies/data_22.json", "valid": "false"}, | |
287 | {"phase": "multiple dependencies subschema", "test": "wrong type other", "schema": "dependencies/schema_2.json", "data": "dependencies/data_23.json", "valid": "false", "errors": "invalid_type"}, | |
288 | {"phase": "multiple dependencies subschema", "test": "wrong type both", "schema": "dependencies/schema_2.json", "data": "dependencies/data_24.json", "valid": "false", "errors": "invalid_type, invalid_type"}, | |
289 | {"phase": "patternProperties validates properties matching a regex", "test": "a single valid match is valid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_00.json", "valid": "true"}, | |
290 | {"phase": "patternProperties validates properties matching a regex", "test": "multiple valid matches is valid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_01.json", "valid": "true"}, | |
291 | {"phase": "patternProperties validates properties matching a regex", "test": "a single invalid match is invalid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_02.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"}, | |
292 | {"phase": "patternProperties validates properties matching a regex", "test": "multiple invalid matches is invalid", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_03.json", "valid": "false", "errors": "invalid_property_pattern, invalid_property_pattern, invalid_type, invalid_type"}, | |
293 | {"phase": "patternProperties validates properties matching a regex", "test": "ignores non-objects", "schema": "patternProperties/schema_0.json", "data": "patternProperties/data_04.json", "valid": "true"}, | |
294 | {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_3.json", "data": "patternProperties/data_24.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
295 | {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_3.json", "data": "patternProperties/data_25.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
296 | {"phase": "patternProperties validates properties matching a regex", "test": "with additionalProperties combination", "schema": "patternProperties/schema_4.json", "data": "patternProperties/data_26.json", "valid": "false", "errors": "additional_property_not_allowed"}, | |
297 | {"phase": "multiple simultaneous patternProperties are validated", "test": "a single valid match is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_10.json", "valid": "true"}, | |
298 | {"phase": "multiple simultaneous patternProperties are validated", "test": "a simultaneous match is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_11.json", "valid": "true"}, | |
299 | {"phase": "multiple simultaneous patternProperties are validated", "test": "multiple matches is valid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_12.json", "valid": "true"}, | |
300 | {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to one is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_13.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"}, | |
301 | {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to the other is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_14.json", "valid": "false", "errors": "number_lte"}, | |
302 | {"phase": "multiple simultaneous patternProperties are validated", "test": "an invalid due to both is invalid", "schema": "patternProperties/schema_1.json", "data": "patternProperties/data_15.json", "valid": "false", "errors": "invalid_type, number_lte"}, | |
303 | {"phase": "regexes are not anchored by default and are case sensitive", "test": "non recognized members are ignored", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_20.json", "valid": "true"}, | |
304 | {"phase": "regexes are not anchored by default and are case sensitive", "test": "recognized members are accounted for", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_21.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"}, | |
305 | {"phase": "regexes are not anchored by default and are case sensitive", "test": "regexes are case sensitive", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_22.json", "valid": "true"}, | |
306 | {"phase": "regexes are not anchored by default and are case sensitive", "test": "regexes are case sensitive, 2", "schema": "patternProperties/schema_2.json", "data": "patternProperties/data_23.json", "valid": "false", "errors": "invalid_property_pattern, invalid_type"}, | |
307 | {"phase": "remote ref", "test": "remote ref valid", "schema": "refRemote/schema_0.json", "data": "refRemote/data_00.json", "valid": "true"}, | |
308 | {"phase": "remote ref", "test": "remote ref invalid", "schema": "refRemote/schema_0.json", "data": "refRemote/data_01.json", "valid": "false", "errors": "invalid_type"}, | |
309 | {"phase": "fragment within remote ref", "test": "remote fragment valid", "schema": "refRemote/schema_1.json", "data": "refRemote/data_10.json", "valid": "true"}, | |
310 | {"phase": "fragment within remote ref", "test": "remote fragment invalid", "schema": "refRemote/schema_1.json", "data": "refRemote/data_11.json", "valid": "false", "errors": "invalid_type"}, | |
311 | {"phase": "ref within remote ref", "test": "ref within ref valid", "schema": "refRemote/schema_2.json", "data": "refRemote/data_20.json", "valid": "true"}, | |
312 | {"phase": "ref within remote ref", "test": "ref within ref invalid", "schema": "refRemote/schema_2.json", "data": "refRemote/data_21.json", "valid": "false", "errors": "invalid_type"}, | |
313 | {"phase": "format validation", "test": "email format is invalid", "schema": "format/schema_0.json", "data": "format/data_00.json", "valid": "false", "errors": "format"}, | |
314 | {"phase": "format validation", "test": "email format is invalid", "schema": "format/schema_0.json", "data": "format/data_01.json", "valid": "false", "errors": "format"}, | |
315 | {"phase": "format validation", "test": "email format valid", "schema": "format/schema_0.json", "data": "format/data_02.json", "valid": "true"}, | |
316 | {"phase": "format validation", "test": "invoice format valid", "schema": "format/schema_1.json", "data": "format/data_03.json", "valid": "true"}, | |
317 | {"phase": "format validation", "test": "invoice format is invalid", "schema": "format/schema_1.json", "data": "format/data_04.json", "valid": "false", "errors": "format"}, | |
318 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_05.json", "valid": "true"}, | |
319 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_06.json", "valid": "true"}, | |
320 | {"phase": "format validation", "test": "date-time format is invalid", "schema": "format/schema_2.json", "data": "format/data_07.json", "valid": "false", "errors": "format"}, | |
321 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_08.json", "valid": "false", "errors": "format"}, | |
322 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_09.json", "valid": "true"}, | |
323 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_10.json", "valid": "true"}, | |
324 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_11.json", "valid": "true"}, | |
325 | {"phase": "format validation", "test": "date-time format is valid", "schema": "format/schema_2.json", "data": "format/data_12.json", "valid": "true"}, | |
326 | {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_13.json", "valid": "true"}, | |
327 | {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_14.json", "valid": "true"}, | |
328 | {"phase": "format validation", "test": "hostname format is valid", "schema": "format/schema_3.json", "data": "format/data_15.json", "valid": "true"}, | |
329 | {"phase": "format validation", "test": "hostname format is invalid", "schema": "format/schema_3.json", "data": "format/data_16.json", "valid": "false", "errors": "format"}, | |
330 | {"phase": "format validation", "test": "hostname format is invalid", "schema": "format/schema_3.json", "data": "format/data_17.json", "valid": "false", "errors": "format"}, | |
331 | {"phase": "format validation", "test": "ipv4 format is valid", "schema": "format/schema_4.json", "data": "format/data_18.json", "valid": "true"}, | |
332 | {"phase": "format validation", "test": "ipv4 format is invalid", "schema": "format/schema_4.json", "data": "format/data_19.json", "valid": "false", "errors": "format"}, | |
333 | {"phase": "format validation", "test": "ipv6 format is valid", "schema": "format/schema_5.json", "data": "format/data_20.json", "valid": "true"}, | |
334 | {"phase": "format validation", "test": "ipv6 format is valid", "schema": "format/schema_5.json", "data": "format/data_21.json", "valid": "true"}, | |
335 | {"phase": "format validation", "test": "ipv6 format is invalid", "schema": "format/schema_5.json", "data": "format/data_22.json", "valid": "false", "errors": "format"}, | |
336 | {"phase": "format validation", "test": "ipv6 format is invalid", "schema": "format/schema_5.json", "data": "format/data_23.json", "valid": "false", "errors": "format"}, | |
337 | {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_24.json", "valid": "true"}, | |
338 | {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_25.json", "valid": "true"}, | |
339 | {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_26.json", "valid": "true"}, | |
340 | {"phase": "format validation", "test": "uri format is valid", "schema": "format/schema_6.json", "data": "format/data_27.json", "valid": "true"}, | |
341 | {"phase": "format validation", "test": "uri format is invalid", "schema": "format/schema_6.json", "data": "format/data_28.json", "valid": "false", "errors": "format"}, | |
342 | {"phase": "format validation", "test": "uri format is invalid", "schema": "format/schema_6.json", "data": "format/data_13.json", "valid": "false", "errors": "format"}, | |
343 | } | |
344 | ||
345 | //TODO Pass failed tests : id(s) as scope for references is not implemented yet | |
346 | //map[string]string{"phase": "change resolution scope", "test": "changed scope ref valid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_30.json", "valid": "true"}, | |
347 | //map[string]string{"phase": "change resolution scope", "test": "changed scope ref invalid", "schema": "refRemote/schema_3.json", "data": "refRemote/data_31.json", "valid": "false"}} | |
348 | ||
349 | // Setup a small http server on localhost:1234 for testing purposes | |
350 | ||
351 | wd, err := os.Getwd() | |
352 | if err != nil { | |
353 | panic(err.Error()) | |
354 | } | |
355 | ||
356 | testwd := wd + "/json_schema_test_suite" | |
357 | ||
358 | go func() { | |
359 | err := http.ListenAndServe(":1234", http.FileServer(http.Dir(testwd+"/refRemote/remoteFiles"))) | |
360 | if err != nil { | |
361 | panic(err.Error()) | |
362 | } | |
363 | }() | |
364 | ||
365 | // Used for remote schema in ref/schema_5.json that defines "regex" type | |
366 | FormatCheckers.Add("regex", alwaysTrueFormatChecker{}) | |
367 | ||
368 | // Custom Formatter | |
369 | FormatCheckers.Add("invoice", invoiceFormatChecker{}) | |
370 | ||
371 | // Launch tests | |
372 | ||
373 | for testJsonIndex, testJson := range JsonSchemaTestSuiteMap { | |
374 | ||
375 | fmt.Printf("Test (%d) | %s :: %s\n", testJsonIndex, testJson["phase"], testJson["test"]) | |
376 | ||
377 | schemaLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["schema"]) | |
378 | documentLoader := NewReferenceLoader("file://" + testwd + "/" + testJson["data"]) | |
379 | ||
380 | // validate | |
381 | result, err := Validate(schemaLoader, documentLoader) | |
382 | if err != nil { | |
383 | t.Errorf("Error (%s)\n", err.Error()) | |
384 | } | |
385 | givenValid := result.Valid() | |
386 | ||
387 | if displayErrorMessages { | |
388 | for vErrI, vErr := range result.Errors() { | |
389 | fmt.Printf(" Error (%d) | %s\n", vErrI, vErr) | |
390 | } | |
391 | } | |
392 | ||
393 | expectedValid, _ := strconv.ParseBool(testJson["valid"]) | |
394 | if givenValid != expectedValid { | |
395 | t.Errorf("Test failed : %s :: %s, expects %t, given %t\n", testJson["phase"], testJson["test"], expectedValid, givenValid) | |
396 | } | |
397 | ||
398 | if !givenValid && testJson["errors"] != "" { | |
399 | expectedErrors := rxSplitErrors.Split(testJson["errors"], -1) | |
400 | errors := result.Errors() | |
401 | if len(errors) != len(expectedErrors) { | |
402 | t.Errorf("Test failed : %s :: %s, expects %d errors, given %d errors\n", testJson["phase"], testJson["test"], len(expectedErrors), len(errors)) | |
403 | } | |
404 | ||
405 | actualErrors := make([]string, 0) | |
406 | for _, e := range result.Errors() { | |
407 | actualErrors = append(actualErrors, e.Type()) | |
408 | } | |
409 | ||
410 | sort.Strings(actualErrors) | |
411 | sort.Strings(expectedErrors) | |
412 | if !reflect.DeepEqual(actualErrors, expectedErrors) { | |
413 | t.Errorf("Test failed : %s :: %s, expected '%s' errors, given '%s' errors\n", testJson["phase"], testJson["test"], strings.Join(expectedErrors, ", "), strings.Join(actualErrors, ", ")) | |
414 | } | |
415 | } | |
416 | ||
417 | } | |
418 | ||
419 | fmt.Printf("\n%d tests performed / %d total tests to perform ( %.2f %% )\n", len(JsonSchemaTestSuiteMap), 248, float32(len(JsonSchemaTestSuiteMap))/248.0*100.0) | |
420 | } | |
421 | 40 | |
422 | 41 | const circularReference = `{ |
423 | 42 | "type": "object", |
564 | 183 | assert.NotNil(t, err, "expected error loading invalid pattern: %T", l) |
565 | 184 | } |
566 | 185 | } |
186 | ||
187 | const refPropertySchema = `{ | |
188 | "$id" : "http://localhost/schema.json", | |
189 | "properties" : { | |
190 | "$id" : { | |
191 | "$id": "http://localhost/foo.json" | |
192 | }, | |
193 | "$ref" : { | |
194 | "const": { | |
195 | "$ref" : "hello.world" | |
196 | } | |
197 | }, | |
198 | "const" : { | |
199 | "$ref" : "#/definitions/$ref" | |
200 | } | |
201 | }, | |
202 | "definitions" : { | |
203 | "$ref" : { | |
204 | "const": { | |
205 | "$ref" : "hello.world" | |
206 | } | |
207 | } | |
208 | }, | |
209 | "dependencies" : { | |
210 | "$ref" : [ "const" ], | |
211 | "const" : [ "$ref" ] | |
212 | } | |
213 | }` | |
214 | ||
215 | func TestRefProperty(t *testing.T) { | |
216 | schemaLoader := NewStringLoader(refPropertySchema) | |
217 | documentLoader := NewStringLoader(`{ | |
218 | "$ref" : { "$ref" : "hello.world" }, | |
219 | "const" : { "$ref" : "hello.world" } | |
220 | }`) | |
221 | // call the target function | |
222 | s, err := NewSchema(schemaLoader) | |
223 | if err != nil { | |
224 | t.Errorf("Got error: %s", err.Error()) | |
225 | } | |
226 | result, err := s.Validate(documentLoader) | |
227 | if err != nil { | |
228 | t.Errorf("Got error: %s", err.Error()) | |
229 | } | |
230 | if !result.Valid() { | |
231 | for _, err := range result.Errors() { | |
232 | fmt.Println(err.String()) | |
233 | } | |
234 | t.Errorf("Got invalid validation result.") | |
235 | } | |
236 | } | |
237 | ||
238 | func TestFragmentLoader(t *testing.T) { | |
239 | wd, err := os.Getwd() | |
240 | ||
241 | if err != nil { | |
242 | panic(err.Error()) | |
243 | } | |
244 | ||
245 | fileName := filepath.Join(wd, "testdata", "extra", "fragment_schema.json") | |
246 | ||
247 | schemaLoader := NewReferenceLoader("file://" + filepath.ToSlash(fileName) + "#/definitions/x") | |
248 | schema, err := NewSchema(schemaLoader) | |
249 | ||
250 | if err != nil { | |
251 | t.Errorf("Encountered error while loading schema: %s", err.Error()) | |
252 | } | |
253 | ||
254 | validDocument := NewStringLoader(`5`) | |
255 | invalidDocument := NewStringLoader(`"a"`) | |
256 | ||
257 | result, err := schema.Validate(validDocument) | |
258 | ||
259 | if assert.Nil(t, err, "Unexpected error while validating document: %T", err) { | |
260 | if !result.Valid() { | |
261 | t.Errorf("Got invalid validation result.") | |
262 | } | |
263 | } | |
264 | ||
265 | result, err = schema.Validate(invalidDocument) | |
266 | ||
267 | if assert.Nil(t, err, "Unexpected error while validating document: %T", err) { | |
268 | if len(result.Errors()) != 1 || result.Errors()[0].Type() != "invalid_type" { | |
269 | t.Errorf("Got invalid validation result.") | |
270 | } | |
271 | } | |
272 | } | |
273 | ||
274 | func TestFileWithSpace(t *testing.T) { | |
275 | wd, err := os.Getwd() | |
276 | ||
277 | if err != nil { | |
278 | panic(err.Error()) | |
279 | } | |
280 | ||
281 | fileName := filepath.Join(wd, "testdata", "extra", "file with space.json") | |
282 | loader := NewReferenceLoader("file://" + filepath.ToSlash(fileName)) | |
283 | ||
284 | json, err := loader.LoadJSON() | |
285 | ||
286 | assert.Nil(t, err, "Unexpected error when trying to load a filepath containing a space") | |
287 | assert.Equal(t, map[string]interface{}{"foo": true}, json, "Contents of the file do not match") | |
288 | } | |
289 | ||
290 | func TestAdditionalPropertiesErrorMessage(t *testing.T) { | |
291 | schema := `{ | |
292 | "$schema": "http://json-schema.org/draft-07/schema#", | |
293 | "type": "object", | |
294 | "properties": { | |
295 | "Device": { | |
296 | "type": "object", | |
297 | "additionalProperties": { | |
298 | "type": "string" | |
299 | } | |
300 | } | |
301 | } | |
302 | }` | |
303 | text := `{ | |
304 | "Device":{ | |
305 | "Color" : true | |
306 | } | |
307 | }` | |
308 | loader := NewBytesLoader([]byte(schema)) | |
309 | result, err := Validate(loader, NewBytesLoader([]byte(text))) | |
310 | if err != nil { | |
311 | t.Fatal(err) | |
312 | } | |
313 | ||
314 | if len(result.Errors()) != 1 { | |
315 | t.Fatal("Expected 1 error but got", len(result.Errors())) | |
316 | } | |
317 | ||
318 | expected := "Device.Color: Invalid type. Expected: string, given: boolean" | |
319 | actual := result.Errors()[0].String() | |
320 | if actual != expected { | |
321 | t.Fatalf("Expected '%s' but got '%s'", expected, actual) | |
322 | } | |
323 | } | |
324 | ||
325 | // Inspired by http://json-schema.org/latest/json-schema-core.html#rfc.section.8.2.3 | |
326 | const locationIndependentSchema = `{ | |
327 | "definitions": { | |
328 | "A": { | |
329 | "$id": "#foo" | |
330 | }, | |
331 | "B": { | |
332 | "$id": "http://example.com/other.json", | |
333 | "definitions": { | |
334 | "X": { | |
335 | "$id": "#bar", | |
336 | "allOf": [false] | |
337 | }, | |
338 | "Y": { | |
339 | "$id": "t/inner.json" | |
340 | } | |
341 | } | |
342 | }, | |
343 | "C": { | |
344 | "$id" : "#frag", | |
345 | "$ref": "http://example.com/other.json#bar" | |
346 | } | |
347 | }, | |
348 | "$ref": "#frag" | |
349 | }` | |
350 | ||
351 | func TestLocationIndependentIdentifier(t *testing.T) { | |
352 | schemaLoader := NewStringLoader(locationIndependentSchema) | |
353 | documentLoader := NewStringLoader(`{}`) | |
354 | ||
355 | s, err := NewSchema(schemaLoader) | |
356 | if err != nil { | |
357 | t.Errorf("Got error: %s", err.Error()) | |
358 | } | |
359 | ||
360 | result, err := s.Validate(documentLoader) | |
361 | if err != nil { | |
362 | t.Errorf("Got error: %s", err.Error()) | |
363 | } | |
364 | ||
365 | if len(result.Errors()) != 2 || result.Errors()[0].Type() != "false" || result.Errors()[1].Type() != "number_all_of" { | |
366 | t.Errorf("Got invalid validation result.") | |
367 | } | |
368 | } | |
369 | ||
370 | const incorrectRefSchema = `{ | |
371 | "$ref" : "#/fail" | |
372 | }` | |
373 | ||
374 | func TestIncorrectRef(t *testing.T) { | |
375 | ||
376 | schemaLoader := NewStringLoader(incorrectRefSchema) | |
377 | s, err := NewSchema(schemaLoader) | |
378 | ||
379 | assert.Nil(t, s) | |
380 | assert.Equal(t, "Object has no key 'fail'", err.Error()) | |
381 | } |
26 | 26 | package gojsonschema |
27 | 27 | |
28 | 28 | import ( |
29 | "errors" | |
29 | "github.com/xeipuuv/gojsonreference" | |
30 | "math/big" | |
30 | 31 | "regexp" |
31 | "strings" | |
32 | ||
33 | "github.com/xeipuuv/gojsonreference" | |
34 | 32 | ) |
35 | 33 | |
34 | // Constants | |
36 | 35 | const ( |
37 | KEY_SCHEMA = "$subSchema" | |
38 | KEY_ID = "$id" | |
36 | KEY_SCHEMA = "$schema" | |
37 | KEY_ID = "id" | |
38 | KEY_ID_NEW = "$id" | |
39 | 39 | KEY_REF = "$ref" |
40 | 40 | KEY_TITLE = "title" |
41 | 41 | KEY_DESCRIPTION = "description" |
45 | 45 | KEY_PROPERTIES = "properties" |
46 | 46 | KEY_PATTERN_PROPERTIES = "patternProperties" |
47 | 47 | KEY_ADDITIONAL_PROPERTIES = "additionalProperties" |
48 | KEY_PROPERTY_NAMES = "propertyNames" | |
48 | 49 | KEY_DEFINITIONS = "definitions" |
49 | 50 | KEY_MULTIPLE_OF = "multipleOf" |
50 | 51 | KEY_MINIMUM = "minimum" |
62 | 63 | KEY_MIN_ITEMS = "minItems" |
63 | 64 | KEY_MAX_ITEMS = "maxItems" |
64 | 65 | KEY_UNIQUE_ITEMS = "uniqueItems" |
66 | KEY_CONTAINS = "contains" | |
67 | KEY_CONST = "const" | |
65 | 68 | KEY_ENUM = "enum" |
66 | 69 | KEY_ONE_OF = "oneOf" |
67 | 70 | KEY_ANY_OF = "anyOf" |
68 | 71 | KEY_ALL_OF = "allOf" |
69 | 72 | KEY_NOT = "not" |
73 | KEY_IF = "if" | |
74 | KEY_THEN = "then" | |
75 | KEY_ELSE = "else" | |
70 | 76 | ) |
71 | 77 | |
72 | 78 | type subSchema struct { |
79 | draft *Draft | |
73 | 80 | |
74 | 81 | // basic subSchema meta properties |
75 | id *string | |
82 | id *gojsonreference.JsonReference | |
76 | 83 | title *string |
77 | 84 | description *string |
78 | 85 | |
79 | 86 | property string |
87 | ||
88 | // Quick pass/fail for boolean schemas | |
89 | pass *bool | |
80 | 90 | |
81 | 91 | // Types associated with the subSchema |
82 | 92 | types jsonSchemaType |
85 | 95 | ref *gojsonreference.JsonReference |
86 | 96 | // Schema referenced |
87 | 97 | refSchema *subSchema |
88 | // Json reference | |
89 | subSchema *gojsonreference.JsonReference | |
90 | 98 | |
91 | 99 | // hierarchy |
92 | 100 | parent *subSchema |
93 | definitions map[string]*subSchema | |
94 | definitionsChildren []*subSchema | |
95 | 101 | itemsChildren []*subSchema |
96 | 102 | itemsChildrenIsSingleSchema bool |
97 | 103 | propertiesChildren []*subSchema |
98 | 104 | |
99 | 105 | // validation : number / integer |
100 | multipleOf *float64 | |
101 | maximum *float64 | |
102 | exclusiveMaximum bool | |
103 | minimum *float64 | |
104 | exclusiveMinimum bool | |
106 | multipleOf *big.Rat | |
107 | maximum *big.Rat | |
108 | exclusiveMaximum *big.Rat | |
109 | minimum *big.Rat | |
110 | exclusiveMinimum *big.Rat | |
105 | 111 | |
106 | 112 | // validation : string |
107 | 113 | minLength *int |
117 | 123 | dependencies map[string]interface{} |
118 | 124 | additionalProperties interface{} |
119 | 125 | patternProperties map[string]*subSchema |
126 | propertyNames *subSchema | |
120 | 127 | |
121 | 128 | // validation : array |
122 | 129 | minItems *int |
123 | 130 | maxItems *int |
124 | 131 | uniqueItems bool |
132 | contains *subSchema | |
125 | 133 | |
126 | 134 | additionalItems interface{} |
127 | 135 | |
128 | 136 | // validation : all |
129 | enum []string | |
137 | _const *string //const is a golang keyword | |
138 | enum []string | |
130 | 139 | |
131 | 140 | // validation : subSchema |
132 | 141 | oneOf []*subSchema |
133 | 142 | anyOf []*subSchema |
134 | 143 | allOf []*subSchema |
135 | 144 | not *subSchema |
145 | _if *subSchema // if/else are golang keywords | |
146 | _then *subSchema | |
147 | _else *subSchema | |
136 | 148 | } |
137 | ||
138 | func (s *subSchema) AddEnum(i interface{}) error { | |
139 | ||
140 | is, err := marshalToJsonString(i) | |
141 | if err != nil { | |
142 | return err | |
143 | } | |
144 | ||
145 | if isStringInSlice(s.enum, *is) { | |
146 | return errors.New(formatErrorDescription( | |
147 | Locale.KeyItemsMustBeUnique(), | |
148 | ErrorDetails{"key": KEY_ENUM}, | |
149 | )) | |
150 | } | |
151 | ||
152 | s.enum = append(s.enum, *is) | |
153 | ||
154 | return nil | |
155 | } | |
156 | ||
157 | func (s *subSchema) ContainsEnum(i interface{}) (bool, error) { | |
158 | ||
159 | is, err := marshalToJsonString(i) | |
160 | if err != nil { | |
161 | return false, err | |
162 | } | |
163 | ||
164 | return isStringInSlice(s.enum, *is), nil | |
165 | } | |
166 | ||
167 | func (s *subSchema) AddOneOf(subSchema *subSchema) { | |
168 | s.oneOf = append(s.oneOf, subSchema) | |
169 | } | |
170 | ||
171 | func (s *subSchema) AddAllOf(subSchema *subSchema) { | |
172 | s.allOf = append(s.allOf, subSchema) | |
173 | } | |
174 | ||
175 | func (s *subSchema) AddAnyOf(subSchema *subSchema) { | |
176 | s.anyOf = append(s.anyOf, subSchema) | |
177 | } | |
178 | ||
179 | func (s *subSchema) SetNot(subSchema *subSchema) { | |
180 | s.not = subSchema | |
181 | } | |
182 | ||
183 | func (s *subSchema) AddRequired(value string) error { | |
184 | ||
185 | if isStringInSlice(s.required, value) { | |
186 | return errors.New(formatErrorDescription( | |
187 | Locale.KeyItemsMustBeUnique(), | |
188 | ErrorDetails{"key": KEY_REQUIRED}, | |
189 | )) | |
190 | } | |
191 | ||
192 | s.required = append(s.required, value) | |
193 | ||
194 | return nil | |
195 | } | |
196 | ||
197 | func (s *subSchema) AddDefinitionChild(child *subSchema) { | |
198 | s.definitionsChildren = append(s.definitionsChildren, child) | |
199 | } | |
200 | ||
201 | func (s *subSchema) AddItemsChild(child *subSchema) { | |
202 | s.itemsChildren = append(s.itemsChildren, child) | |
203 | } | |
204 | ||
205 | func (s *subSchema) AddPropertiesChild(child *subSchema) { | |
206 | s.propertiesChildren = append(s.propertiesChildren, child) | |
207 | } | |
208 | ||
209 | func (s *subSchema) PatternPropertiesString() string { | |
210 | ||
211 | if s.patternProperties == nil || len(s.patternProperties) == 0 { | |
212 | return STRING_UNDEFINED // should never happen | |
213 | } | |
214 | ||
215 | patternPropertiesKeySlice := []string{} | |
216 | for pk := range s.patternProperties { | |
217 | patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`) | |
218 | } | |
219 | ||
220 | if len(patternPropertiesKeySlice) == 1 { | |
221 | return patternPropertiesKeySlice[0] | |
222 | } | |
223 | ||
224 | return "[" + strings.Join(patternPropertiesKeySlice, ",") + "]" | |
225 | ||
226 | } |
0 | [ | |
1 | { | |
2 | "description": "additionalItems as schema", | |
3 | "schema": { | |
4 | "items": [{}], | |
5 | "additionalItems": {"type": "integer"} | |
6 | }, | |
7 | "tests": [ | |
8 | { | |
9 | "description": "additional items match schema", | |
10 | "data": [ null, 2, 3, 4 ], | |
11 | "valid": true | |
12 | }, | |
13 | { | |
14 | "description": "additional items do not match schema", | |
15 | "data": [ null, 2, 3, "foo" ], | |
16 | "valid": false | |
17 | } | |
18 | ] | |
19 | }, | |
20 | { | |
21 | "description": "items is schema, no additionalItems", | |
22 | "schema": { | |
23 | "items": {}, | |
24 | "additionalItems": false | |
25 | }, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "all items match schema", | |
29 | "data": [ 1, 2, 3, 4, 5 ], | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "array of items with no additionalItems", | |
36 | "schema": { | |
37 | "items": [{}, {}, {}], | |
38 | "additionalItems": false | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "fewer number of items present", | |
43 | "data": [ 1, 2 ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "equal number of items present", | |
48 | "data": [ 1, 2, 3 ], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "additional items are not permitted", | |
53 | "data": [ 1, 2, 3, 4 ], | |
54 | "valid": false | |
55 | } | |
56 | ] | |
57 | }, | |
58 | { | |
59 | "description": "additionalItems as false without items", | |
60 | "schema": {"additionalItems": false}, | |
61 | "tests": [ | |
62 | { | |
63 | "description": | |
64 | "items defaults to empty schema so everything is valid", | |
65 | "data": [ 1, 2, 3, 4, 5 ], | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "ignores non-arrays", | |
70 | "data": {"foo" : "bar"}, | |
71 | "valid": true | |
72 | } | |
73 | ] | |
74 | }, | |
75 | { | |
76 | "description": "additionalItems are allowed by default", | |
77 | "schema": {"items": [{"type": "integer"}]}, | |
78 | "tests": [ | |
79 | { | |
80 | "description": "only the first item is validated", | |
81 | "data": [1, "foo", false], | |
82 | "valid": true | |
83 | } | |
84 | ] | |
85 | } | |
86 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "additionalProperties being false does not allow other properties", | |
4 | "schema": { | |
5 | "properties": {"foo": {}, "bar": {}}, | |
6 | "patternProperties": { "^v": {} }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "no additional properties is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "an additional property is invalid", | |
17 | "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobarbaz", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "patternProperties are not additional properties", | |
37 | "data": {"foo":1, "vroom": 2}, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "additionalProperties allows a schema which should validate", | |
45 | "schema": { | |
46 | "properties": {"foo": {}, "bar": {}}, | |
47 | "additionalProperties": {"type": "boolean"} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "no additional properties is valid", | |
52 | "data": {"foo": 1}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "an additional valid property is valid", | |
57 | "data": {"foo" : 1, "bar" : 2, "quux" : true}, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "an additional invalid property is invalid", | |
62 | "data": {"foo" : 1, "bar" : 2, "quux" : 12}, | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": | |
69 | "additionalProperties can exist by itself", | |
70 | "schema": { | |
71 | "additionalProperties": {"type": "boolean"} | |
72 | }, | |
73 | "tests": [ | |
74 | { | |
75 | "description": "an additional valid property is valid", | |
76 | "data": {"foo" : true}, | |
77 | "valid": true | |
78 | }, | |
79 | { | |
80 | "description": "an additional invalid property is invalid", | |
81 | "data": {"foo" : 1}, | |
82 | "valid": false | |
83 | } | |
84 | ] | |
85 | }, | |
86 | { | |
87 | "description": "additionalProperties are allowed by default", | |
88 | "schema": {"properties": {"foo": {}, "bar": {}}}, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "additional properties are allowed", | |
92 | "data": {"foo": 1, "bar": 2, "quux": true}, | |
93 | "valid": true | |
94 | } | |
95 | ] | |
96 | } | |
97 | ] |
0 | [ | |
1 | { | |
2 | "description": "allOf", | |
3 | "schema": { | |
4 | "allOf": [ | |
5 | { | |
6 | "properties": { | |
7 | "bar": {"type": "integer"} | |
8 | }, | |
9 | "required": ["bar"] | |
10 | }, | |
11 | { | |
12 | "properties": { | |
13 | "foo": {"type": "string"} | |
14 | }, | |
15 | "required": ["foo"] | |
16 | } | |
17 | ] | |
18 | }, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "allOf", | |
22 | "data": {"foo": "baz", "bar": 2}, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "mismatch second", | |
27 | "data": {"foo": "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "mismatch first", | |
32 | "data": {"bar": 2}, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "wrong type", | |
37 | "data": {"foo": "baz", "bar": "quux"}, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "allOf with base schema", | |
44 | "schema": { | |
45 | "properties": {"bar": {"type": "integer"}}, | |
46 | "required": ["bar"], | |
47 | "allOf" : [ | |
48 | { | |
49 | "properties": { | |
50 | "foo": {"type": "string"} | |
51 | }, | |
52 | "required": ["foo"] | |
53 | }, | |
54 | { | |
55 | "properties": { | |
56 | "baz": {"type": "null"} | |
57 | }, | |
58 | "required": ["baz"] | |
59 | } | |
60 | ] | |
61 | }, | |
62 | "tests": [ | |
63 | { | |
64 | "description": "valid", | |
65 | "data": {"foo": "quux", "bar": 2, "baz": null}, | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "mismatch base schema", | |
70 | "data": {"foo": "quux", "baz": null}, | |
71 | "valid": false | |
72 | }, | |
73 | { | |
74 | "description": "mismatch first allOf", | |
75 | "data": {"bar": 2, "baz": null}, | |
76 | "valid": false | |
77 | }, | |
78 | { | |
79 | "description": "mismatch second allOf", | |
80 | "data": {"foo": "quux", "bar": 2}, | |
81 | "valid": false | |
82 | }, | |
83 | { | |
84 | "description": "mismatch both", | |
85 | "data": {"bar": 2}, | |
86 | "valid": false | |
87 | } | |
88 | ] | |
89 | }, | |
90 | { | |
91 | "description": "allOf simple types", | |
92 | "schema": { | |
93 | "allOf": [ | |
94 | {"maximum": 30}, | |
95 | {"minimum": 20} | |
96 | ] | |
97 | }, | |
98 | "tests": [ | |
99 | { | |
100 | "description": "valid", | |
101 | "data": 25, | |
102 | "valid": true | |
103 | }, | |
104 | { | |
105 | "description": "mismatch one", | |
106 | "data": 35, | |
107 | "valid": false | |
108 | } | |
109 | ] | |
110 | } | |
111 | ] |
0 | [ | |
1 | { | |
2 | "description": "anyOf", | |
3 | "schema": { | |
4 | "anyOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first anyOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second anyOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both anyOf valid", | |
26 | "data": 3, | |
27 | "valid": true | |
28 | }, | |
29 | { | |
30 | "description": "neither anyOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "anyOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "anyOf" : [ | |
41 | { | |
42 | "maxLength": 2 | |
43 | }, | |
44 | { | |
45 | "minLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one anyOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both anyOf invalid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "anyOf complex types", | |
69 | "schema": { | |
70 | "anyOf": [ | |
71 | { | |
72 | "properties": { | |
73 | "bar": {"type": "integer"} | |
74 | }, | |
75 | "required": ["bar"] | |
76 | }, | |
77 | { | |
78 | "properties": { | |
79 | "foo": {"type": "string"} | |
80 | }, | |
81 | "required": ["foo"] | |
82 | } | |
83 | ] | |
84 | }, | |
85 | "tests": [ | |
86 | { | |
87 | "description": "first anyOf valid (complex)", | |
88 | "data": {"bar": 2}, | |
89 | "valid": true | |
90 | }, | |
91 | { | |
92 | "description": "second anyOf valid (complex)", | |
93 | "data": {"foo": "baz"}, | |
94 | "valid": true | |
95 | }, | |
96 | { | |
97 | "description": "both anyOf valid (complex)", | |
98 | "data": {"foo": "baz", "bar": 2}, | |
99 | "valid": true | |
100 | }, | |
101 | { | |
102 | "description": "neither anyOf valid (complex)", | |
103 | "data": {"foo": 2, "bar": "quux"}, | |
104 | "valid": false | |
105 | } | |
106 | ] | |
107 | } | |
108 | ] |
0 | [ | |
1 | { | |
2 | "description": "invalid type for default", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": { | |
6 | "type": "integer", | |
7 | "default": [] | |
8 | } | |
9 | } | |
10 | }, | |
11 | "tests": [ | |
12 | { | |
13 | "description": "valid when property is specified", | |
14 | "data": {"foo": 13}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "still valid when the invalid default is used", | |
19 | "data": {}, | |
20 | "valid": true | |
21 | } | |
22 | ] | |
23 | }, | |
24 | { | |
25 | "description": "invalid string value for default", | |
26 | "schema": { | |
27 | "properties": { | |
28 | "bar": { | |
29 | "type": "string", | |
30 | "minLength": 4, | |
31 | "default": "bad" | |
32 | } | |
33 | } | |
34 | }, | |
35 | "tests": [ | |
36 | { | |
37 | "description": "valid when property is specified", | |
38 | "data": {"bar": "good"}, | |
39 | "valid": true | |
40 | }, | |
41 | { | |
42 | "description": "still valid when the invalid default is used", | |
43 | "data": {}, | |
44 | "valid": true | |
45 | } | |
46 | ] | |
47 | } | |
48 | ] |
0 | [ | |
1 | { | |
2 | "description": "valid definition", | |
3 | "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "valid definition schema", | |
7 | "data": { | |
8 | "definitions": { | |
9 | "foo": {"type": "integer"} | |
10 | } | |
11 | }, | |
12 | "valid": true | |
13 | } | |
14 | ] | |
15 | }, | |
16 | { | |
17 | "description": "invalid definition", | |
18 | "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "invalid definition schema", | |
22 | "data": { | |
23 | "definitions": { | |
24 | "foo": {"type": 1} | |
25 | } | |
26 | }, | |
27 | "valid": false | |
28 | } | |
29 | ] | |
30 | } | |
31 | ] |
0 | [ | |
1 | { | |
2 | "description": "dependencies", | |
3 | "schema": { | |
4 | "dependencies": {"bar": ["foo"]} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "neither", | |
9 | "data": {}, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "nondependant", | |
14 | "data": {"foo": 1}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "with dependency", | |
19 | "data": {"foo": 1, "bar": 2}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "missing dependency", | |
24 | "data": {"bar": 2}, | |
25 | "valid": false | |
26 | }, | |
27 | { | |
28 | "description": "ignores arrays", | |
29 | "data": ["bar"], | |
30 | "valid": true | |
31 | }, | |
32 | { | |
33 | "description": "ignores strings", | |
34 | "data": "foobar", | |
35 | "valid": true | |
36 | }, | |
37 | { | |
38 | "description": "ignores other non-objects", | |
39 | "data": 12, | |
40 | "valid": true | |
41 | } | |
42 | ] | |
43 | }, | |
44 | { | |
45 | "description": "multiple dependencies", | |
46 | "schema": { | |
47 | "dependencies": {"quux": ["foo", "bar"]} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "neither", | |
52 | "data": {}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "nondependants", | |
57 | "data": {"foo": 1, "bar": 2}, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "with dependencies", | |
62 | "data": {"foo": 1, "bar": 2, "quux": 3}, | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "missing dependency", | |
67 | "data": {"foo": 1, "quux": 2}, | |
68 | "valid": false | |
69 | }, | |
70 | { | |
71 | "description": "missing other dependency", | |
72 | "data": {"bar": 1, "quux": 2}, | |
73 | "valid": false | |
74 | }, | |
75 | { | |
76 | "description": "missing both dependencies", | |
77 | "data": {"quux": 1}, | |
78 | "valid": false | |
79 | } | |
80 | ] | |
81 | }, | |
82 | { | |
83 | "description": "multiple dependencies subschema", | |
84 | "schema": { | |
85 | "dependencies": { | |
86 | "bar": { | |
87 | "properties": { | |
88 | "foo": {"type": "integer"}, | |
89 | "bar": {"type": "integer"} | |
90 | } | |
91 | } | |
92 | } | |
93 | }, | |
94 | "tests": [ | |
95 | { | |
96 | "description": "valid", | |
97 | "data": {"foo": 1, "bar": 2}, | |
98 | "valid": true | |
99 | }, | |
100 | { | |
101 | "description": "no dependency", | |
102 | "data": {"foo": "quux"}, | |
103 | "valid": true | |
104 | }, | |
105 | { | |
106 | "description": "wrong type", | |
107 | "data": {"foo": "quux", "bar": 2}, | |
108 | "valid": false | |
109 | }, | |
110 | { | |
111 | "description": "wrong type other", | |
112 | "data": {"foo": 2, "bar": "quux"}, | |
113 | "valid": false | |
114 | }, | |
115 | { | |
116 | "description": "wrong type both", | |
117 | "data": {"foo": "quux", "bar": "quux"}, | |
118 | "valid": false | |
119 | } | |
120 | ] | |
121 | } | |
122 | ] |
0 | [ | |
1 | { | |
2 | "description": "simple enum validation", | |
3 | "schema": {"enum": [1, 2, 3]}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "one of the enum is valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "something else is invalid", | |
12 | "data": 4, | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "heterogeneous enum validation", | |
19 | "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "one of the enum is valid", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "something else is invalid", | |
28 | "data": null, | |
29 | "valid": false | |
30 | }, | |
31 | { | |
32 | "description": "objects are deep compared", | |
33 | "data": {"foo": false}, | |
34 | "valid": false | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "enums in properties", | |
40 | "schema": { | |
41 | "type":"object", | |
42 | "properties": { | |
43 | "foo": {"enum":["foo"]}, | |
44 | "bar": {"enum":["bar"]} | |
45 | }, | |
46 | "required": ["bar"] | |
47 | }, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "both properties are valid", | |
51 | "data": {"foo":"foo", "bar":"bar"}, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "missing optional property is valid", | |
56 | "data": {"bar":"bar"}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "missing required property is invalid", | |
61 | "data": {"foo":"foo"}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "missing all properties is invalid", | |
66 | "data": {}, | |
67 | "valid": false | |
68 | } | |
69 | ] | |
70 | } | |
71 | ] |
0 | [ | |
1 | { | |
2 | "description": "a schema given for items", | |
3 | "schema": { | |
4 | "items": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "valid items", | |
9 | "data": [ 1, 2, 3 ], | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "wrong type of items", | |
14 | "data": [1, "x"], | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "ignores non-arrays", | |
19 | "data": {"foo" : "bar"}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "JavaScript pseudo-array is valid", | |
24 | "data": { | |
25 | "0": "invalid", | |
26 | "length": 1 | |
27 | }, | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "an array of schemas for items", | |
34 | "schema": { | |
35 | "items": [ | |
36 | {"type": "integer"}, | |
37 | {"type": "string"} | |
38 | ] | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "correct types", | |
43 | "data": [ 1, "foo" ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "wrong types", | |
48 | "data": [ "foo", 1 ], | |
49 | "valid": false | |
50 | }, | |
51 | { | |
52 | "description": "incomplete array of items", | |
53 | "data": [ 1 ], | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "array with additional items", | |
58 | "data": [ 1, "foo", true ], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "empty array", | |
63 | "data": [ ], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "JavaScript pseudo-array is valid", | |
68 | "data": { | |
69 | "0": "invalid", | |
70 | "1": "valid", | |
71 | "length": 2 | |
72 | }, | |
73 | "valid": true | |
74 | } | |
75 | ] | |
76 | } | |
77 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxItems validation", | |
3 | "schema": {"maxItems": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": [1], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1, 2], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": [1, 2, 3], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "foobar", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxLength validation", | |
3 | "schema": {"maxLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": "f", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 100, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "two supplementary Unicode code points is long enough", | |
27 | "data": "\uD83D\uDCA9\uD83D\uDCA9", | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxProperties validation", | |
3 | "schema": {"maxProperties": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": {"foo": 1}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1, "bar": 2}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": {"foo": 1, "bar": 2, "baz": 3}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobar", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "maximum validation", | |
3 | "schema": {"maximum": 3.0}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "below the maximum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 3.0, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "above the maximum is invalid", | |
17 | "data": 3.5, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | }, | |
27 | { | |
28 | "description": "exclusiveMaximum validation", | |
29 | "schema": { | |
30 | "maximum": 3.0, | |
31 | "exclusiveMaximum": true | |
32 | }, | |
33 | "tests": [ | |
34 | { | |
35 | "description": "below the maximum is still valid", | |
36 | "data": 2.2, | |
37 | "valid": true | |
38 | }, | |
39 | { | |
40 | "description": "boundary point is invalid", | |
41 | "data": 3.0, | |
42 | "valid": false | |
43 | } | |
44 | ] | |
45 | } | |
46 | ] |
0 | [ | |
1 | { | |
2 | "description": "minItems validation", | |
3 | "schema": {"minItems": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": [], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "minLength validation", | |
3 | "schema": {"minLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": "foo", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": "f", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 1, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "one supplementary Unicode code point is not long enough", | |
27 | "data": "\uD83D\uDCA9", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "minProperties validation", | |
3 | "schema": {"minProperties": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": {"foo": 1, "bar": 2}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": {}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "minimum validation", | |
3 | "schema": {"minimum": 1.1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "above the minimum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 1.1, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "below the minimum is invalid", | |
17 | "data": 0.6, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | }, | |
27 | { | |
28 | "description": "exclusiveMinimum validation", | |
29 | "schema": { | |
30 | "minimum": 1.1, | |
31 | "exclusiveMinimum": true | |
32 | }, | |
33 | "tests": [ | |
34 | { | |
35 | "description": "above the minimum is still valid", | |
36 | "data": 1.2, | |
37 | "valid": true | |
38 | }, | |
39 | { | |
40 | "description": "boundary point is invalid", | |
41 | "data": 1.1, | |
42 | "valid": false | |
43 | } | |
44 | ] | |
45 | } | |
46 | ] |
0 | [ | |
1 | { | |
2 | "description": "by int", | |
3 | "schema": {"multipleOf": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "int by int", | |
7 | "data": 10, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "int by int fail", | |
12 | "data": 7, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-numbers", | |
17 | "data": "foo", | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "by number", | |
24 | "schema": {"multipleOf": 1.5}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "zero is multiple of anything", | |
28 | "data": 0, | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "4.5 is multiple of 1.5", | |
33 | "data": 4.5, | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "35 is not multiple of 1.5", | |
38 | "data": 35, | |
39 | "valid": false | |
40 | } | |
41 | ] | |
42 | }, | |
43 | { | |
44 | "description": "by small number", | |
45 | "schema": {"multipleOf": 0.0001}, | |
46 | "tests": [ | |
47 | { | |
48 | "description": "0.0075 is multiple of 0.0001", | |
49 | "data": 0.0075, | |
50 | "valid": true | |
51 | }, | |
52 | { | |
53 | "description": "0.00751 is not multiple of 0.0001", | |
54 | "data": 0.00751, | |
55 | "valid": false | |
56 | } | |
57 | ] | |
58 | } | |
59 | ] |
0 | [ | |
1 | { | |
2 | "description": "not", | |
3 | "schema": { | |
4 | "not": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "allowed", | |
9 | "data": "foo", | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "disallowed", | |
14 | "data": 1, | |
15 | "valid": false | |
16 | } | |
17 | ] | |
18 | }, | |
19 | { | |
20 | "description": "not multiple types", | |
21 | "schema": { | |
22 | "not": {"type": ["integer", "boolean"]} | |
23 | }, | |
24 | "tests": [ | |
25 | { | |
26 | "description": "valid", | |
27 | "data": "foo", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "mismatch", | |
32 | "data": 1, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "other mismatch", | |
37 | "data": true, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "not more complex schema", | |
44 | "schema": { | |
45 | "not": { | |
46 | "type": "object", | |
47 | "properties": { | |
48 | "foo": { | |
49 | "type": "string" | |
50 | } | |
51 | } | |
52 | } | |
53 | }, | |
54 | "tests": [ | |
55 | { | |
56 | "description": "match", | |
57 | "data": 1, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "other match", | |
62 | "data": {"foo": 1}, | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "mismatch", | |
67 | "data": {"foo": "bar"}, | |
68 | "valid": false | |
69 | } | |
70 | ] | |
71 | }, | |
72 | { | |
73 | "description": "forbidden property", | |
74 | "schema": { | |
75 | "properties": { | |
76 | "foo": { | |
77 | "not": {} | |
78 | } | |
79 | } | |
80 | }, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "property present", | |
84 | "data": {"foo": 1, "bar": 2}, | |
85 | "valid": false | |
86 | }, | |
87 | { | |
88 | "description": "property absent", | |
89 | "data": {"bar": 1, "baz": 2}, | |
90 | "valid": true | |
91 | } | |
92 | ] | |
93 | } | |
94 | ||
95 | ] |
0 | [ | |
1 | { | |
2 | "description": "oneOf", | |
3 | "schema": { | |
4 | "oneOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first oneOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second oneOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both oneOf valid", | |
26 | "data": 3, | |
27 | "valid": false | |
28 | }, | |
29 | { | |
30 | "description": "neither oneOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "oneOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "oneOf" : [ | |
41 | { | |
42 | "minLength": 2 | |
43 | }, | |
44 | { | |
45 | "maxLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one oneOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both oneOf valid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "oneOf complex types", | |
69 | "schema": { | |
70 | "oneOf": [ | |
71 | { | |
72 | "properties": { | |
73 | "bar": {"type": "integer"} | |
74 | }, | |
75 | "required": ["bar"] | |
76 | }, | |
77 | { | |
78 | "properties": { | |
79 | "foo": {"type": "string"} | |
80 | }, | |
81 | "required": ["foo"] | |
82 | } | |
83 | ] | |
84 | }, | |
85 | "tests": [ | |
86 | { | |
87 | "description": "first oneOf valid (complex)", | |
88 | "data": {"bar": 2}, | |
89 | "valid": true | |
90 | }, | |
91 | { | |
92 | "description": "second oneOf valid (complex)", | |
93 | "data": {"foo": "baz"}, | |
94 | "valid": true | |
95 | }, | |
96 | { | |
97 | "description": "both oneOf valid (complex)", | |
98 | "data": {"foo": "baz", "bar": 2}, | |
99 | "valid": false | |
100 | }, | |
101 | { | |
102 | "description": "neither oneOf valid (complex)", | |
103 | "data": {"foo": 2, "bar": "quux"}, | |
104 | "valid": false | |
105 | } | |
106 | ] | |
107 | } | |
108 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a bignum is an integer", | |
7 | "data": 12345678910111213141516171819202122232425262728293031, | |
8 | "valid": true | |
9 | } | |
10 | ] | |
11 | }, | |
12 | { | |
13 | "description": "number", | |
14 | "schema": {"type": "number"}, | |
15 | "tests": [ | |
16 | { | |
17 | "description": "a bignum is a number", | |
18 | "data": 98249283749234923498293171823948729348710298301928331, | |
19 | "valid": true | |
20 | } | |
21 | ] | |
22 | }, | |
23 | { | |
24 | "description": "integer", | |
25 | "schema": {"type": "integer"}, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "a negative bignum is an integer", | |
29 | "data": -12345678910111213141516171819202122232425262728293031, | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "number", | |
36 | "schema": {"type": "number"}, | |
37 | "tests": [ | |
38 | { | |
39 | "description": "a negative bignum is a number", | |
40 | "data": -98249283749234923498293171823948729348710298301928331, | |
41 | "valid": true | |
42 | } | |
43 | ] | |
44 | }, | |
45 | { | |
46 | "description": "string", | |
47 | "schema": {"type": "string"}, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "a bignum is not a string", | |
51 | "data": 98249283749234923498293171823948729348710298301928331, | |
52 | "valid": false | |
53 | } | |
54 | ] | |
55 | }, | |
56 | { | |
57 | "description": "integer comparison", | |
58 | "schema": {"maximum": 18446744073709551615}, | |
59 | "tests": [ | |
60 | { | |
61 | "description": "comparison works for high numbers", | |
62 | "data": 18446744073709551600, | |
63 | "valid": true | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "float comparison with high precision", | |
69 | "schema": { | |
70 | "maximum": 972783798187987123879878123.18878137, | |
71 | "exclusiveMaximum": true | |
72 | }, | |
73 | "tests": [ | |
74 | { | |
75 | "description": "comparison works for high numbers", | |
76 | "data": 972783798187987123879878123.188781371, | |
77 | "valid": false | |
78 | } | |
79 | ] | |
80 | }, | |
81 | { | |
82 | "description": "integer comparison", | |
83 | "schema": {"minimum": -18446744073709551615}, | |
84 | "tests": [ | |
85 | { | |
86 | "description": "comparison works for very negative numbers", | |
87 | "data": -18446744073709551600, | |
88 | "valid": true | |
89 | } | |
90 | ] | |
91 | }, | |
92 | { | |
93 | "description": "float comparison with high precision on negative numbers", | |
94 | "schema": { | |
95 | "minimum": -972783798187987123879878123.18878137, | |
96 | "exclusiveMinimum": true | |
97 | }, | |
98 | "tests": [ | |
99 | { | |
100 | "description": "comparison works for very negative numbers", | |
101 | "data": -972783798187987123879878123.188781371, | |
102 | "valid": false | |
103 | } | |
104 | ] | |
105 | } | |
106 | ] |
0 | [ | |
1 | { | |
2 | "description": "ECMA 262 regex non-compliance", | |
3 | "schema": { "format": "regex" }, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "ECMA 262 has no support for \\Z anchor from .NET", | |
7 | "data": "^\\S(|(.|\\n)*\\S)\\Z", | |
8 | "valid": false | |
9 | } | |
10 | ] | |
11 | } | |
12 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of date-time strings", | |
3 | "schema": {"format": "date-time"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid date-time string", | |
7 | "data": "1963-06-19T08:30:06.283185Z", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid date-time string", | |
12 | "data": "06/19/1963 08:30:06 PST", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "only RFC3339 not all of ISO 8601 are valid", | |
17 | "data": "2013-350T01:01:01", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "validation of URIs", | |
24 | "schema": {"format": "uri"}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "a valid URL with anchor tag", | |
28 | "data": "http://foo.bar/?baz=qux#quux", | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "a valid URL with anchor tag and parantheses", | |
33 | "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "a valid URL with URL-encoded stuff", | |
38 | "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", | |
39 | "valid": true | |
40 | }, | |
41 | { | |
42 | "description": "a valid puny-coded URL ", | |
43 | "data": "http://xn--nw2a.xn--j6w193g/", | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "a valid URL with many special characters", | |
48 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "a valid URL based on IPv4", | |
53 | "data": "http://223.255.255.254", | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "a valid URL with ftp scheme", | |
58 | "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a valid URL for a simple text file", | |
63 | "data": "http://www.ietf.org/rfc/rfc2396.txt", | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "a valid URL ", | |
68 | "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "a valid mailto URI", | |
73 | "data": "mailto:John.Doe@example.com", | |
74 | "valid": true | |
75 | }, | |
76 | { | |
77 | "description": "a valid newsgroup URI", | |
78 | "data": "news:comp.infosystems.www.servers.unix", | |
79 | "valid": true | |
80 | }, | |
81 | { | |
82 | "description": "a valid tel URI", | |
83 | "data": "tel:+1-816-555-1212", | |
84 | "valid": true | |
85 | }, | |
86 | { | |
87 | "description": "a valid URN", | |
88 | "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", | |
89 | "valid": true | |
90 | }, | |
91 | { | |
92 | "description": "an invalid protocol-relative URI Reference", | |
93 | "data": "//foo.bar/?baz=qux#quux", | |
94 | "valid": false | |
95 | }, | |
96 | { | |
97 | "description": "an invalid relative URI Reference", | |
98 | "data": "/abc", | |
99 | "valid": false | |
100 | }, | |
101 | { | |
102 | "description": "an invalid URI", | |
103 | "data": "\\\\WINDOWS\\fileshare", | |
104 | "valid": false | |
105 | }, | |
106 | { | |
107 | "description": "an invalid URI though valid URI reference", | |
108 | "data": "abc", | |
109 | "valid": false | |
110 | }, | |
111 | { | |
112 | "description": "an invalid URI with spaces", | |
113 | "data": "http:// shouldfail.com", | |
114 | "valid": false | |
115 | }, | |
116 | { | |
117 | "description": "an invalid URI with spaces and missing scheme", | |
118 | "data": ":// should fail", | |
119 | "valid": false | |
120 | } | |
121 | ] | |
122 | }, | |
123 | { | |
124 | "description": "validation of e-mail addresses", | |
125 | "schema": {"format": "email"}, | |
126 | "tests": [ | |
127 | { | |
128 | "description": "a valid e-mail address", | |
129 | "data": "joe.bloggs@example.com", | |
130 | "valid": true | |
131 | }, | |
132 | { | |
133 | "description": "an invalid e-mail address", | |
134 | "data": "2962", | |
135 | "valid": false | |
136 | } | |
137 | ] | |
138 | }, | |
139 | { | |
140 | "description": "validation of IP addresses", | |
141 | "schema": {"format": "ipv4"}, | |
142 | "tests": [ | |
143 | { | |
144 | "description": "a valid IP address", | |
145 | "data": "192.168.0.1", | |
146 | "valid": true | |
147 | }, | |
148 | { | |
149 | "description": "an IP address with too many components", | |
150 | "data": "127.0.0.0.1", | |
151 | "valid": false | |
152 | }, | |
153 | { | |
154 | "description": "an IP address with out-of-range values", | |
155 | "data": "256.256.256.256", | |
156 | "valid": false | |
157 | }, | |
158 | { | |
159 | "description": "an IP address without 4 components", | |
160 | "data": "127.0", | |
161 | "valid": false | |
162 | }, | |
163 | { | |
164 | "description": "an IP address as an integer", | |
165 | "data": "0x7f000001", | |
166 | "valid": false | |
167 | } | |
168 | ] | |
169 | }, | |
170 | { | |
171 | "description": "validation of IPv6 addresses", | |
172 | "schema": {"format": "ipv6"}, | |
173 | "tests": [ | |
174 | { | |
175 | "description": "a valid IPv6 address", | |
176 | "data": "::1", | |
177 | "valid": true | |
178 | }, | |
179 | { | |
180 | "description": "an IPv6 address with out-of-range values", | |
181 | "data": "12345::", | |
182 | "valid": false | |
183 | }, | |
184 | { | |
185 | "description": "an IPv6 address with too many components", | |
186 | "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", | |
187 | "valid": false | |
188 | }, | |
189 | { | |
190 | "description": "an IPv6 address containing illegal characters", | |
191 | "data": "::laptop", | |
192 | "valid": false | |
193 | } | |
194 | ] | |
195 | }, | |
196 | { | |
197 | "description": "validation of host names", | |
198 | "schema": {"format": "hostname"}, | |
199 | "tests": [ | |
200 | { | |
201 | "description": "a valid host name", | |
202 | "data": "www.example.com", | |
203 | "valid": true | |
204 | }, | |
205 | { | |
206 | "description": "a host name starting with an illegal character", | |
207 | "data": "-a-host-name-that-starts-with--", | |
208 | "valid": false | |
209 | }, | |
210 | { | |
211 | "description": "a host name containing illegal characters", | |
212 | "data": "not_a_valid_host_name", | |
213 | "valid": false | |
214 | }, | |
215 | { | |
216 | "description": "a host name with a component too long", | |
217 | "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", | |
218 | "valid": false | |
219 | } | |
220 | ] | |
221 | } | |
222 | ] |
0 | [ | |
1 | { | |
2 | "description": "some languages do not distinguish between different types of numeric value", | |
3 | "schema": { | |
4 | "type": "integer" | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "a float is not an integer even without fractional part", | |
9 | "data": 1.0, | |
10 | "valid": false | |
11 | } | |
12 | ] | |
13 | } | |
14 | ] |
0 | [ | |
1 | { | |
2 | "description": "pattern validation", | |
3 | "schema": {"pattern": "^a*$"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a matching pattern is valid", | |
7 | "data": "aaa", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a non-matching pattern is invalid", | |
12 | "data": "abc", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-strings", | |
17 | "data": true, | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "pattern is not anchored", | |
24 | "schema": {"pattern": "a+"}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "matches a substring", | |
28 | "data": "xxaayy", | |
29 | "valid": true | |
30 | } | |
31 | ] | |
32 | } | |
33 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "patternProperties validates properties matching a regex", | |
4 | "schema": { | |
5 | "patternProperties": { | |
6 | "f.*o": {"type": "integer"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "a single valid match is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "multiple valid matches is valid", | |
17 | "data": {"foo": 1, "foooooo" : 2}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a single invalid match is invalid", | |
22 | "data": {"foo": "bar", "fooooo": 2}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "multiple invalid matches is invalid", | |
27 | "data": {"foo": "bar", "foooooo" : "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": [], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores strings", | |
37 | "data": "", | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "ignores other non-objects", | |
42 | "data": 12, | |
43 | "valid": true | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "multiple simultaneous patternProperties are validated", | |
49 | "schema": { | |
50 | "patternProperties": { | |
51 | "a*": {"type": "integer"}, | |
52 | "aaa*": {"maximum": 20} | |
53 | } | |
54 | }, | |
55 | "tests": [ | |
56 | { | |
57 | "description": "a single valid match is valid", | |
58 | "data": {"a": 21}, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a simultaneous match is valid", | |
63 | "data": {"aaaa": 18}, | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "multiple matches is valid", | |
68 | "data": {"a": 21, "aaaa": 18}, | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "an invalid due to one is invalid", | |
73 | "data": {"a": "bar"}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an invalid due to the other is invalid", | |
78 | "data": {"aaaa": 31}, | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "an invalid due to both is invalid", | |
83 | "data": {"aaa": "foo", "aaaa": 31}, | |
84 | "valid": false | |
85 | } | |
86 | ] | |
87 | }, | |
88 | { | |
89 | "description": "regexes are not anchored by default and are case sensitive", | |
90 | "schema": { | |
91 | "patternProperties": { | |
92 | "[0-9]{2,}": { "type": "boolean" }, | |
93 | "X_": { "type": "string" } | |
94 | } | |
95 | }, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "non recognized members are ignored", | |
99 | "data": { "answer 1": "42" }, | |
100 | "valid": true | |
101 | }, | |
102 | { | |
103 | "description": "recognized members are accounted for", | |
104 | "data": { "a31b": null }, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "regexes are case sensitive", | |
109 | "data": { "a_x_3": 3 }, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "regexes are case sensitive, 2", | |
114 | "data": { "a_X_3": 3 }, | |
115 | "valid": false | |
116 | } | |
117 | ] | |
118 | } | |
119 | ] |
0 | [ | |
1 | { | |
2 | "description": "object properties validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"type": "integer"}, | |
6 | "bar": {"type": "string"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "both properties present and valid is valid", | |
12 | "data": {"foo": 1, "bar": "baz"}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "one property invalid is invalid", | |
17 | "data": {"foo": 1, "bar": {}}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "both properties invalid is invalid", | |
22 | "data": {"foo": [], "bar": {}}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "doesn't invalidate other properties", | |
27 | "data": {"quux": []}, | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": [], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores other non-objects", | |
37 | "data": 12, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "properties, patternProperties, additionalProperties interaction", | |
45 | "schema": { | |
46 | "properties": { | |
47 | "foo": {"type": "array", "maxItems": 3}, | |
48 | "bar": {"type": "array"} | |
49 | }, | |
50 | "patternProperties": {"f.o": {"minItems": 2}}, | |
51 | "additionalProperties": {"type": "integer"} | |
52 | }, | |
53 | "tests": [ | |
54 | { | |
55 | "description": "property validates property", | |
56 | "data": {"foo": [1, 2]}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "property invalidates property", | |
61 | "data": {"foo": [1, 2, 3, 4]}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "patternProperty invalidates property", | |
66 | "data": {"foo": []}, | |
67 | "valid": false | |
68 | }, | |
69 | { | |
70 | "description": "patternProperty validates nonproperty", | |
71 | "data": {"fxo": [1, 2]}, | |
72 | "valid": true | |
73 | }, | |
74 | { | |
75 | "description": "patternProperty invalidates nonproperty", | |
76 | "data": {"fxo": []}, | |
77 | "valid": false | |
78 | }, | |
79 | { | |
80 | "description": "additionalProperty ignores property", | |
81 | "data": {"bar": []}, | |
82 | "valid": true | |
83 | }, | |
84 | { | |
85 | "description": "additionalProperty validates others", | |
86 | "data": {"quux": 3}, | |
87 | "valid": true | |
88 | }, | |
89 | { | |
90 | "description": "additionalProperty invalidates others", | |
91 | "data": {"quux": "foo"}, | |
92 | "valid": false | |
93 | } | |
94 | ] | |
95 | } | |
96 | ] |
0 | [ | |
1 | { | |
2 | "description": "root pointer ref", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"$ref": "#"} | |
6 | }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "match", | |
12 | "data": {"foo": false}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "recursive match", | |
17 | "data": {"foo": {"foo": false}}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "mismatch", | |
22 | "data": {"bar": false}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "recursive mismatch", | |
27 | "data": {"foo": {"bar": false}}, | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "relative pointer ref to object", | |
34 | "schema": { | |
35 | "properties": { | |
36 | "foo": {"type": "integer"}, | |
37 | "bar": {"$ref": "#/properties/foo"} | |
38 | } | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "match", | |
43 | "data": {"bar": 3}, | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "mismatch", | |
48 | "data": {"bar": true}, | |
49 | "valid": false | |
50 | } | |
51 | ] | |
52 | }, | |
53 | { | |
54 | "description": "relative pointer ref to array", | |
55 | "schema": { | |
56 | "items": [ | |
57 | {"type": "integer"}, | |
58 | {"$ref": "#/items/0"} | |
59 | ] | |
60 | }, | |
61 | "tests": [ | |
62 | { | |
63 | "description": "match array", | |
64 | "data": [1, 2], | |
65 | "valid": true | |
66 | }, | |
67 | { | |
68 | "description": "mismatch array", | |
69 | "data": [1, "foo"], | |
70 | "valid": false | |
71 | } | |
72 | ] | |
73 | }, | |
74 | { | |
75 | "description": "escaped pointer ref", | |
76 | "schema": { | |
77 | "tilda~field": {"type": "integer"}, | |
78 | "slash/field": {"type": "integer"}, | |
79 | "percent%field": {"type": "integer"}, | |
80 | "properties": { | |
81 | "tilda": {"$ref": "#/tilda~0field"}, | |
82 | "slash": {"$ref": "#/slash~1field"}, | |
83 | "percent": {"$ref": "#/percent%25field"} | |
84 | } | |
85 | }, | |
86 | "tests": [ | |
87 | { | |
88 | "description": "slash invalid", | |
89 | "data": {"slash": "aoeu"}, | |
90 | "valid": false | |
91 | }, | |
92 | { | |
93 | "description": "tilda invalid", | |
94 | "data": {"tilda": "aoeu"}, | |
95 | "valid": false | |
96 | }, | |
97 | { | |
98 | "description": "percent invalid", | |
99 | "data": {"percent": "aoeu"}, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "slash valid", | |
104 | "data": {"slash": 123}, | |
105 | "valid": true | |
106 | }, | |
107 | { | |
108 | "description": "tilda valid", | |
109 | "data": {"tilda": 123}, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "percent valid", | |
114 | "data": {"percent": 123}, | |
115 | "valid": true | |
116 | } | |
117 | ] | |
118 | }, | |
119 | { | |
120 | "description": "nested refs", | |
121 | "schema": { | |
122 | "definitions": { | |
123 | "a": {"type": "integer"}, | |
124 | "b": {"$ref": "#/definitions/a"}, | |
125 | "c": {"$ref": "#/definitions/b"} | |
126 | }, | |
127 | "$ref": "#/definitions/c" | |
128 | }, | |
129 | "tests": [ | |
130 | { | |
131 | "description": "nested ref valid", | |
132 | "data": 5, | |
133 | "valid": true | |
134 | }, | |
135 | { | |
136 | "description": "nested ref invalid", | |
137 | "data": "a", | |
138 | "valid": false | |
139 | } | |
140 | ] | |
141 | }, | |
142 | { | |
143 | "description": "ref overrides any sibling keywords", | |
144 | "schema": { | |
145 | "definitions": { | |
146 | "reffed": { | |
147 | "type": "array" | |
148 | } | |
149 | }, | |
150 | "properties": { | |
151 | "foo": { | |
152 | "$ref": "#/definitions/reffed", | |
153 | "maxItems": 2 | |
154 | } | |
155 | } | |
156 | }, | |
157 | "tests": [ | |
158 | { | |
159 | "description": "ref valid", | |
160 | "data": { "foo": [] }, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "ref valid, maxItems ignored", | |
165 | "data": { "foo": [ 1, 2, 3] }, | |
166 | "valid": true | |
167 | }, | |
168 | { | |
169 | "description": "ref invalid", | |
170 | "data": { "foo": "string" }, | |
171 | "valid": false | |
172 | } | |
173 | ] | |
174 | }, | |
175 | { | |
176 | "description": "remote ref, containing refs itself", | |
177 | "schema": {"$ref": "http://json-schema.org/draft-04/schema#"}, | |
178 | "tests": [ | |
179 | { | |
180 | "description": "remote ref valid", | |
181 | "data": {"minLength": 1}, | |
182 | "valid": true | |
183 | }, | |
184 | { | |
185 | "description": "remote ref invalid", | |
186 | "data": {"minLength": -1}, | |
187 | "valid": false | |
188 | } | |
189 | ] | |
190 | }, | |
191 | { | |
192 | "description": "property named $ref that is not a reference", | |
193 | "schema": { | |
194 | "properties": { | |
195 | "$ref": {"type": "string"} | |
196 | } | |
197 | }, | |
198 | "tests": [ | |
199 | { | |
200 | "description": "property named $ref valid", | |
201 | "data": {"$ref": "a"}, | |
202 | "valid": true | |
203 | }, | |
204 | { | |
205 | "description": "property named $ref invalid", | |
206 | "data": {"$ref": 2}, | |
207 | "valid": false | |
208 | } | |
209 | ] | |
210 | }, | |
211 | { | |
212 | "description": "Recursive references between schemas", | |
213 | "schema": { | |
214 | "id": "http://localhost:1234/tree", | |
215 | "description": "tree of nodes", | |
216 | "type": "object", | |
217 | "properties": { | |
218 | "meta": {"type": "string"}, | |
219 | "nodes": { | |
220 | "type": "array", | |
221 | "items": {"$ref": "node"} | |
222 | } | |
223 | }, | |
224 | "required": ["meta", "nodes"], | |
225 | "definitions": { | |
226 | "node": { | |
227 | "id": "http://localhost:1234/node", | |
228 | "description": "node", | |
229 | "type": "object", | |
230 | "properties": { | |
231 | "value": {"type": "number"}, | |
232 | "subtree": {"$ref": "tree"} | |
233 | }, | |
234 | "required": ["value"] | |
235 | } | |
236 | } | |
237 | }, | |
238 | "tests": [ | |
239 | { | |
240 | "description": "valid tree", | |
241 | "data": { | |
242 | "meta": "root", | |
243 | "nodes": [ | |
244 | { | |
245 | "value": 1, | |
246 | "subtree": { | |
247 | "meta": "child", | |
248 | "nodes": [ | |
249 | {"value": 1.1}, | |
250 | {"value": 1.2} | |
251 | ] | |
252 | } | |
253 | }, | |
254 | { | |
255 | "value": 2, | |
256 | "subtree": { | |
257 | "meta": "child", | |
258 | "nodes": [ | |
259 | {"value": 2.1}, | |
260 | {"value": 2.2} | |
261 | ] | |
262 | } | |
263 | } | |
264 | ] | |
265 | }, | |
266 | "valid": true | |
267 | }, | |
268 | { | |
269 | "description": "invalid tree", | |
270 | "data": { | |
271 | "meta": "root", | |
272 | "nodes": [ | |
273 | { | |
274 | "value": 1, | |
275 | "subtree": { | |
276 | "meta": "child", | |
277 | "nodes": [ | |
278 | {"value": "string is invalid"}, | |
279 | {"value": 1.2} | |
280 | ] | |
281 | } | |
282 | }, | |
283 | { | |
284 | "value": 2, | |
285 | "subtree": { | |
286 | "meta": "child", | |
287 | "nodes": [ | |
288 | {"value": 2.1}, | |
289 | {"value": 2.2} | |
290 | ] | |
291 | } | |
292 | } | |
293 | ] | |
294 | }, | |
295 | "valid": false | |
296 | } | |
297 | ] | |
298 | } | |
299 | ] |
0 | [ | |
1 | { | |
2 | "description": "remote ref", | |
3 | "schema": {"$ref": "http://localhost:1234/integer.json"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "remote ref valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "remote ref invalid", | |
12 | "data": "a", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "fragment within remote ref", | |
19 | "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "remote fragment valid", | |
23 | "data": 1, | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "remote fragment invalid", | |
28 | "data": "a", | |
29 | "valid": false | |
30 | } | |
31 | ] | |
32 | }, | |
33 | { | |
34 | "description": "ref within remote ref", | |
35 | "schema": { | |
36 | "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" | |
37 | }, | |
38 | "tests": [ | |
39 | { | |
40 | "description": "ref within ref valid", | |
41 | "data": 1, | |
42 | "valid": true | |
43 | }, | |
44 | { | |
45 | "description": "ref within ref invalid", | |
46 | "data": "a", | |
47 | "valid": false | |
48 | } | |
49 | ] | |
50 | }, | |
51 | { | |
52 | "description": "base URI change", | |
53 | "schema": { | |
54 | "id": "http://localhost:1234/", | |
55 | "items": { | |
56 | "id": "folder/", | |
57 | "items": {"$ref": "folderInteger.json"} | |
58 | } | |
59 | }, | |
60 | "tests": [ | |
61 | { | |
62 | "description": "base URI change ref valid", | |
63 | "data": [[1]], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "base URI change ref invalid", | |
68 | "data": [["a"]], | |
69 | "valid": false | |
70 | } | |
71 | ] | |
72 | }, | |
73 | { | |
74 | "description": "base URI change - change folder", | |
75 | "schema": { | |
76 | "id": "http://localhost:1234/scope_change_defs1.json", | |
77 | "type" : "object", | |
78 | "properties": { | |
79 | "list": {"$ref": "#/definitions/baz"} | |
80 | }, | |
81 | "definitions": { | |
82 | "baz": { | |
83 | "id": "folder/", | |
84 | "type": "array", | |
85 | "items": {"$ref": "folderInteger.json"} | |
86 | } | |
87 | } | |
88 | }, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "number is valid", | |
92 | "data": {"list": [1]}, | |
93 | "valid": true | |
94 | }, | |
95 | { | |
96 | "description": "string is invalid", | |
97 | "data": {"list": ["a"]}, | |
98 | "valid": false | |
99 | } | |
100 | ] | |
101 | }, | |
102 | { | |
103 | "description": "base URI change - change folder in subschema", | |
104 | "schema": { | |
105 | "id": "http://localhost:1234/scope_change_defs2.json", | |
106 | "type" : "object", | |
107 | "properties": { | |
108 | "list": {"$ref": "#/definitions/baz/definitions/bar"} | |
109 | }, | |
110 | "definitions": { | |
111 | "baz": { | |
112 | "id": "folder/", | |
113 | "definitions": { | |
114 | "bar": { | |
115 | "type": "array", | |
116 | "items": {"$ref": "folderInteger.json"} | |
117 | } | |
118 | } | |
119 | } | |
120 | } | |
121 | }, | |
122 | "tests": [ | |
123 | { | |
124 | "description": "number is valid", | |
125 | "data": {"list": [1]}, | |
126 | "valid": true | |
127 | }, | |
128 | { | |
129 | "description": "string is invalid", | |
130 | "data": {"list": ["a"]}, | |
131 | "valid": false | |
132 | } | |
133 | ] | |
134 | }, | |
135 | { | |
136 | "description": "root ref in remote ref", | |
137 | "schema": { | |
138 | "id": "http://localhost:1234/object", | |
139 | "type": "object", | |
140 | "properties": { | |
141 | "name": {"$ref": "name.json#/definitions/orNull"} | |
142 | } | |
143 | }, | |
144 | "tests": [ | |
145 | { | |
146 | "description": "string is valid", | |
147 | "data": { | |
148 | "name": "foo" | |
149 | }, | |
150 | "valid": true | |
151 | }, | |
152 | { | |
153 | "description": "null is valid", | |
154 | "data": { | |
155 | "name": null | |
156 | }, | |
157 | "valid": true | |
158 | }, | |
159 | { | |
160 | "description": "object is invalid", | |
161 | "data": { | |
162 | "name": { | |
163 | "name": null | |
164 | } | |
165 | }, | |
166 | "valid": false | |
167 | } | |
168 | ] | |
169 | } | |
170 | ] |
0 | [ | |
1 | { | |
2 | "description": "required validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {}, | |
6 | "bar": {} | |
7 | }, | |
8 | "required": ["foo"] | |
9 | }, | |
10 | "tests": [ | |
11 | { | |
12 | "description": "present required property is valid", | |
13 | "data": {"foo": 1}, | |
14 | "valid": true | |
15 | }, | |
16 | { | |
17 | "description": "non-present required property is invalid", | |
18 | "data": {"bar": 1}, | |
19 | "valid": false | |
20 | }, | |
21 | { | |
22 | "description": "ignores arrays", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "ignores strings", | |
28 | "data": "", | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "ignores other non-objects", | |
33 | "data": 12, | |
34 | "valid": true | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "required default validation", | |
40 | "schema": { | |
41 | "properties": { | |
42 | "foo": {} | |
43 | } | |
44 | }, | |
45 | "tests": [ | |
46 | { | |
47 | "description": "not required by default", | |
48 | "data": {}, | |
49 | "valid": true | |
50 | } | |
51 | ] | |
52 | } | |
53 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer type matches integers", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "an integer is an integer", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a float is not an integer", | |
12 | "data": 1.1, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "a string is not an integer", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "a string is still not an integer, even if it looks like one", | |
22 | "data": "1", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "an object is not an integer", | |
27 | "data": {}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "an array is not an integer", | |
32 | "data": [], | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "a boolean is not an integer", | |
37 | "data": true, | |
38 | "valid": false | |
39 | }, | |
40 | { | |
41 | "description": "null is not an integer", | |
42 | "data": null, | |
43 | "valid": false | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "number type matches numbers", | |
49 | "schema": {"type": "number"}, | |
50 | "tests": [ | |
51 | { | |
52 | "description": "an integer is a number", | |
53 | "data": 1, | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "a float is a number", | |
58 | "data": 1.1, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a string is not a number", | |
63 | "data": "foo", | |
64 | "valid": false | |
65 | }, | |
66 | { | |
67 | "description": "a string is still not a number, even if it looks like one", | |
68 | "data": "1", | |
69 | "valid": false | |
70 | }, | |
71 | { | |
72 | "description": "an object is not a number", | |
73 | "data": {}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an array is not a number", | |
78 | "data": [], | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "a boolean is not a number", | |
83 | "data": true, | |
84 | "valid": false | |
85 | }, | |
86 | { | |
87 | "description": "null is not a number", | |
88 | "data": null, | |
89 | "valid": false | |
90 | } | |
91 | ] | |
92 | }, | |
93 | { | |
94 | "description": "string type matches strings", | |
95 | "schema": {"type": "string"}, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "1 is not a string", | |
99 | "data": 1, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "a float is not a string", | |
104 | "data": 1.1, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "a string is a string", | |
109 | "data": "foo", | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "a string is still a string, even if it looks like a number", | |
114 | "data": "1", | |
115 | "valid": true | |
116 | }, | |
117 | { | |
118 | "description": "an object is not a string", | |
119 | "data": {}, | |
120 | "valid": false | |
121 | }, | |
122 | { | |
123 | "description": "an array is not a string", | |
124 | "data": [], | |
125 | "valid": false | |
126 | }, | |
127 | { | |
128 | "description": "a boolean is not a string", | |
129 | "data": true, | |
130 | "valid": false | |
131 | }, | |
132 | { | |
133 | "description": "null is not a string", | |
134 | "data": null, | |
135 | "valid": false | |
136 | } | |
137 | ] | |
138 | }, | |
139 | { | |
140 | "description": "object type matches objects", | |
141 | "schema": {"type": "object"}, | |
142 | "tests": [ | |
143 | { | |
144 | "description": "an integer is not an object", | |
145 | "data": 1, | |
146 | "valid": false | |
147 | }, | |
148 | { | |
149 | "description": "a float is not an object", | |
150 | "data": 1.1, | |
151 | "valid": false | |
152 | }, | |
153 | { | |
154 | "description": "a string is not an object", | |
155 | "data": "foo", | |
156 | "valid": false | |
157 | }, | |
158 | { | |
159 | "description": "an object is an object", | |
160 | "data": {}, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "an array is not an object", | |
165 | "data": [], | |
166 | "valid": false | |
167 | }, | |
168 | { | |
169 | "description": "a boolean is not an object", | |
170 | "data": true, | |
171 | "valid": false | |
172 | }, | |
173 | { | |
174 | "description": "null is not an object", | |
175 | "data": null, | |
176 | "valid": false | |
177 | } | |
178 | ] | |
179 | }, | |
180 | { | |
181 | "description": "array type matches arrays", | |
182 | "schema": {"type": "array"}, | |
183 | "tests": [ | |
184 | { | |
185 | "description": "an integer is not an array", | |
186 | "data": 1, | |
187 | "valid": false | |
188 | }, | |
189 | { | |
190 | "description": "a float is not an array", | |
191 | "data": 1.1, | |
192 | "valid": false | |
193 | }, | |
194 | { | |
195 | "description": "a string is not an array", | |
196 | "data": "foo", | |
197 | "valid": false | |
198 | }, | |
199 | { | |
200 | "description": "an object is not an array", | |
201 | "data": {}, | |
202 | "valid": false | |
203 | }, | |
204 | { | |
205 | "description": "an array is an array", | |
206 | "data": [], | |
207 | "valid": true | |
208 | }, | |
209 | { | |
210 | "description": "a boolean is not an array", | |
211 | "data": true, | |
212 | "valid": false | |
213 | }, | |
214 | { | |
215 | "description": "null is not an array", | |
216 | "data": null, | |
217 | "valid": false | |
218 | } | |
219 | ] | |
220 | }, | |
221 | { | |
222 | "description": "boolean type matches booleans", | |
223 | "schema": {"type": "boolean"}, | |
224 | "tests": [ | |
225 | { | |
226 | "description": "an integer is not a boolean", | |
227 | "data": 1, | |
228 | "valid": false | |
229 | }, | |
230 | { | |
231 | "description": "a float is not a boolean", | |
232 | "data": 1.1, | |
233 | "valid": false | |
234 | }, | |
235 | { | |
236 | "description": "a string is not a boolean", | |
237 | "data": "foo", | |
238 | "valid": false | |
239 | }, | |
240 | { | |
241 | "description": "an object is not a boolean", | |
242 | "data": {}, | |
243 | "valid": false | |
244 | }, | |
245 | { | |
246 | "description": "an array is not a boolean", | |
247 | "data": [], | |
248 | "valid": false | |
249 | }, | |
250 | { | |
251 | "description": "a boolean is a boolean", | |
252 | "data": true, | |
253 | "valid": true | |
254 | }, | |
255 | { | |
256 | "description": "null is not a boolean", | |
257 | "data": null, | |
258 | "valid": false | |
259 | } | |
260 | ] | |
261 | }, | |
262 | { | |
263 | "description": "null type matches only the null object", | |
264 | "schema": {"type": "null"}, | |
265 | "tests": [ | |
266 | { | |
267 | "description": "an integer is not null", | |
268 | "data": 1, | |
269 | "valid": false | |
270 | }, | |
271 | { | |
272 | "description": "a float is not null", | |
273 | "data": 1.1, | |
274 | "valid": false | |
275 | }, | |
276 | { | |
277 | "description": "a string is not null", | |
278 | "data": "foo", | |
279 | "valid": false | |
280 | }, | |
281 | { | |
282 | "description": "an object is not null", | |
283 | "data": {}, | |
284 | "valid": false | |
285 | }, | |
286 | { | |
287 | "description": "an array is not null", | |
288 | "data": [], | |
289 | "valid": false | |
290 | }, | |
291 | { | |
292 | "description": "a boolean is not null", | |
293 | "data": true, | |
294 | "valid": false | |
295 | }, | |
296 | { | |
297 | "description": "null is null", | |
298 | "data": null, | |
299 | "valid": true | |
300 | } | |
301 | ] | |
302 | }, | |
303 | { | |
304 | "description": "multiple types can be specified in an array", | |
305 | "schema": {"type": ["integer", "string"]}, | |
306 | "tests": [ | |
307 | { | |
308 | "description": "an integer is valid", | |
309 | "data": 1, | |
310 | "valid": true | |
311 | }, | |
312 | { | |
313 | "description": "a string is valid", | |
314 | "data": "foo", | |
315 | "valid": true | |
316 | }, | |
317 | { | |
318 | "description": "a float is invalid", | |
319 | "data": 1.1, | |
320 | "valid": false | |
321 | }, | |
322 | { | |
323 | "description": "an object is invalid", | |
324 | "data": {}, | |
325 | "valid": false | |
326 | }, | |
327 | { | |
328 | "description": "an array is invalid", | |
329 | "data": [], | |
330 | "valid": false | |
331 | }, | |
332 | { | |
333 | "description": "a boolean is invalid", | |
334 | "data": true, | |
335 | "valid": false | |
336 | }, | |
337 | { | |
338 | "description": "null is invalid", | |
339 | "data": null, | |
340 | "valid": false | |
341 | } | |
342 | ] | |
343 | } | |
344 | ] |
0 | [ | |
1 | { | |
2 | "description": "uniqueItems validation", | |
3 | "schema": {"uniqueItems": true}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "unique array of integers is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "non-unique array of integers is invalid", | |
12 | "data": [1, 1], | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "numbers are unique if mathematically unequal", | |
17 | "data": [1.0, 1.00, 1], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "unique array of objects is valid", | |
22 | "data": [{"foo": "bar"}, {"foo": "baz"}], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "non-unique array of objects is invalid", | |
27 | "data": [{"foo": "bar"}, {"foo": "bar"}], | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "unique array of nested objects is valid", | |
32 | "data": [ | |
33 | {"foo": {"bar" : {"baz" : true}}}, | |
34 | {"foo": {"bar" : {"baz" : false}}} | |
35 | ], | |
36 | "valid": true | |
37 | }, | |
38 | { | |
39 | "description": "non-unique array of nested objects is invalid", | |
40 | "data": [ | |
41 | {"foo": {"bar" : {"baz" : true}}}, | |
42 | {"foo": {"bar" : {"baz" : true}}} | |
43 | ], | |
44 | "valid": false | |
45 | }, | |
46 | { | |
47 | "description": "unique array of arrays is valid", | |
48 | "data": [["foo"], ["bar"]], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "non-unique array of arrays is invalid", | |
53 | "data": [["foo"], ["foo"]], | |
54 | "valid": false | |
55 | }, | |
56 | { | |
57 | "description": "1 and true are unique", | |
58 | "data": [1, true], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "0 and false are unique", | |
63 | "data": [0, false], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "unique heterogeneous types are valid", | |
68 | "data": [{}, [1], true, null, 1], | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "non-unique heterogeneous types are invalid", | |
73 | "data": [{}, [1], true, null, {}, 1], | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | } | |
78 | ] |
0 | [ | |
1 | { | |
2 | "description": "additionalItems as schema", | |
3 | "schema": { | |
4 | "items": [{}], | |
5 | "additionalItems": {"type": "integer"} | |
6 | }, | |
7 | "tests": [ | |
8 | { | |
9 | "description": "additional items match schema", | |
10 | "data": [ null, 2, 3, 4 ], | |
11 | "valid": true | |
12 | }, | |
13 | { | |
14 | "description": "additional items do not match schema", | |
15 | "data": [ null, 2, 3, "foo" ], | |
16 | "valid": false | |
17 | } | |
18 | ] | |
19 | }, | |
20 | { | |
21 | "description": "items is schema, no additionalItems", | |
22 | "schema": { | |
23 | "items": {}, | |
24 | "additionalItems": false | |
25 | }, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "all items match schema", | |
29 | "data": [ 1, 2, 3, 4, 5 ], | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "array of items with no additionalItems", | |
36 | "schema": { | |
37 | "items": [{}, {}, {}], | |
38 | "additionalItems": false | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "fewer number of items present", | |
43 | "data": [ 1, 2 ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "equal number of items present", | |
48 | "data": [ 1, 2, 3 ], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "additional items are not permitted", | |
53 | "data": [ 1, 2, 3, 4 ], | |
54 | "valid": false | |
55 | } | |
56 | ] | |
57 | }, | |
58 | { | |
59 | "description": "additionalItems as false without items", | |
60 | "schema": {"additionalItems": false}, | |
61 | "tests": [ | |
62 | { | |
63 | "description": | |
64 | "items defaults to empty schema so everything is valid", | |
65 | "data": [ 1, 2, 3, 4, 5 ], | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "ignores non-arrays", | |
70 | "data": {"foo" : "bar"}, | |
71 | "valid": true | |
72 | } | |
73 | ] | |
74 | }, | |
75 | { | |
76 | "description": "additionalItems are allowed by default", | |
77 | "schema": {"items": [{"type": "integer"}]}, | |
78 | "tests": [ | |
79 | { | |
80 | "description": "only the first item is validated", | |
81 | "data": [1, "foo", false], | |
82 | "valid": true | |
83 | } | |
84 | ] | |
85 | } | |
86 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "additionalProperties being false does not allow other properties", | |
4 | "schema": { | |
5 | "properties": {"foo": {}, "bar": {}}, | |
6 | "patternProperties": { "^v": {} }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "no additional properties is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "an additional property is invalid", | |
17 | "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobarbaz", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "patternProperties are not additional properties", | |
37 | "data": {"foo":1, "vroom": 2}, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "additionalProperties allows a schema which should validate", | |
45 | "schema": { | |
46 | "properties": {"foo": {}, "bar": {}}, | |
47 | "additionalProperties": {"type": "boolean"} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "no additional properties is valid", | |
52 | "data": {"foo": 1}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "an additional valid property is valid", | |
57 | "data": {"foo" : 1, "bar" : 2, "quux" : true}, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "an additional invalid property is invalid", | |
62 | "data": {"foo" : 1, "bar" : 2, "quux" : 12}, | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": | |
69 | "additionalProperties can exist by itself", | |
70 | "schema": { | |
71 | "additionalProperties": {"type": "boolean"} | |
72 | }, | |
73 | "tests": [ | |
74 | { | |
75 | "description": "an additional valid property is valid", | |
76 | "data": {"foo" : true}, | |
77 | "valid": true | |
78 | }, | |
79 | { | |
80 | "description": "an additional invalid property is invalid", | |
81 | "data": {"foo" : 1}, | |
82 | "valid": false | |
83 | } | |
84 | ] | |
85 | }, | |
86 | { | |
87 | "description": "additionalProperties are allowed by default", | |
88 | "schema": {"properties": {"foo": {}, "bar": {}}}, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "additional properties are allowed", | |
92 | "data": {"foo": 1, "bar": 2, "quux": true}, | |
93 | "valid": true | |
94 | } | |
95 | ] | |
96 | } | |
97 | ] |
0 | [ | |
1 | { | |
2 | "description": "allOf", | |
3 | "schema": { | |
4 | "allOf": [ | |
5 | { | |
6 | "properties": { | |
7 | "bar": {"type": "integer"} | |
8 | }, | |
9 | "required": ["bar"] | |
10 | }, | |
11 | { | |
12 | "properties": { | |
13 | "foo": {"type": "string"} | |
14 | }, | |
15 | "required": ["foo"] | |
16 | } | |
17 | ] | |
18 | }, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "allOf", | |
22 | "data": {"foo": "baz", "bar": 2}, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "mismatch second", | |
27 | "data": {"foo": "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "mismatch first", | |
32 | "data": {"bar": 2}, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "wrong type", | |
37 | "data": {"foo": "baz", "bar": "quux"}, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "allOf with base schema", | |
44 | "schema": { | |
45 | "properties": {"bar": {"type": "integer"}}, | |
46 | "required": ["bar"], | |
47 | "allOf" : [ | |
48 | { | |
49 | "properties": { | |
50 | "foo": {"type": "string"} | |
51 | }, | |
52 | "required": ["foo"] | |
53 | }, | |
54 | { | |
55 | "properties": { | |
56 | "baz": {"type": "null"} | |
57 | }, | |
58 | "required": ["baz"] | |
59 | } | |
60 | ] | |
61 | }, | |
62 | "tests": [ | |
63 | { | |
64 | "description": "valid", | |
65 | "data": {"foo": "quux", "bar": 2, "baz": null}, | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "mismatch base schema", | |
70 | "data": {"foo": "quux", "baz": null}, | |
71 | "valid": false | |
72 | }, | |
73 | { | |
74 | "description": "mismatch first allOf", | |
75 | "data": {"bar": 2, "baz": null}, | |
76 | "valid": false | |
77 | }, | |
78 | { | |
79 | "description": "mismatch second allOf", | |
80 | "data": {"foo": "quux", "bar": 2}, | |
81 | "valid": false | |
82 | }, | |
83 | { | |
84 | "description": "mismatch both", | |
85 | "data": {"bar": 2}, | |
86 | "valid": false | |
87 | } | |
88 | ] | |
89 | }, | |
90 | { | |
91 | "description": "allOf simple types", | |
92 | "schema": { | |
93 | "allOf": [ | |
94 | {"maximum": 30}, | |
95 | {"minimum": 20} | |
96 | ] | |
97 | }, | |
98 | "tests": [ | |
99 | { | |
100 | "description": "valid", | |
101 | "data": 25, | |
102 | "valid": true | |
103 | }, | |
104 | { | |
105 | "description": "mismatch one", | |
106 | "data": 35, | |
107 | "valid": false | |
108 | } | |
109 | ] | |
110 | }, | |
111 | { | |
112 | "description": "allOf with boolean schemas, all true", | |
113 | "schema": {"allOf": [true, true]}, | |
114 | "tests": [ | |
115 | { | |
116 | "description": "any value is valid", | |
117 | "data": "foo", | |
118 | "valid": true | |
119 | } | |
120 | ] | |
121 | }, | |
122 | { | |
123 | "description": "allOf with boolean schemas, some false", | |
124 | "schema": {"allOf": [true, false]}, | |
125 | "tests": [ | |
126 | { | |
127 | "description": "any value is invalid", | |
128 | "data": "foo", | |
129 | "valid": false | |
130 | } | |
131 | ] | |
132 | }, | |
133 | { | |
134 | "description": "allOf with boolean schemas, all false", | |
135 | "schema": {"allOf": [false, false]}, | |
136 | "tests": [ | |
137 | { | |
138 | "description": "any value is invalid", | |
139 | "data": "foo", | |
140 | "valid": false | |
141 | } | |
142 | ] | |
143 | } | |
144 | ] |
0 | [ | |
1 | { | |
2 | "description": "anyOf", | |
3 | "schema": { | |
4 | "anyOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first anyOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second anyOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both anyOf valid", | |
26 | "data": 3, | |
27 | "valid": true | |
28 | }, | |
29 | { | |
30 | "description": "neither anyOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "anyOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "anyOf" : [ | |
41 | { | |
42 | "maxLength": 2 | |
43 | }, | |
44 | { | |
45 | "minLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one anyOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both anyOf invalid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "anyOf with boolean schemas, all true", | |
69 | "schema": {"anyOf": [true, true]}, | |
70 | "tests": [ | |
71 | { | |
72 | "description": "any value is valid", | |
73 | "data": "foo", | |
74 | "valid": true | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "anyOf with boolean schemas, some true", | |
80 | "schema": {"anyOf": [true, false]}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any value is valid", | |
84 | "data": "foo", | |
85 | "valid": true | |
86 | } | |
87 | ] | |
88 | }, | |
89 | { | |
90 | "description": "anyOf with boolean schemas, all false", | |
91 | "schema": {"anyOf": [false, false]}, | |
92 | "tests": [ | |
93 | { | |
94 | "description": "any value is invalid", | |
95 | "data": "foo", | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "anyOf complex types", | |
102 | "schema": { | |
103 | "anyOf": [ | |
104 | { | |
105 | "properties": { | |
106 | "bar": {"type": "integer"} | |
107 | }, | |
108 | "required": ["bar"] | |
109 | }, | |
110 | { | |
111 | "properties": { | |
112 | "foo": {"type": "string"} | |
113 | }, | |
114 | "required": ["foo"] | |
115 | } | |
116 | ] | |
117 | }, | |
118 | "tests": [ | |
119 | { | |
120 | "description": "first anyOf valid (complex)", | |
121 | "data": {"bar": 2}, | |
122 | "valid": true | |
123 | }, | |
124 | { | |
125 | "description": "second anyOf valid (complex)", | |
126 | "data": {"foo": "baz"}, | |
127 | "valid": true | |
128 | }, | |
129 | { | |
130 | "description": "both anyOf valid (complex)", | |
131 | "data": {"foo": "baz", "bar": 2}, | |
132 | "valid": true | |
133 | }, | |
134 | { | |
135 | "description": "neither anyOf valid (complex)", | |
136 | "data": {"foo": 2, "bar": "quux"}, | |
137 | "valid": false | |
138 | } | |
139 | ] | |
140 | } | |
141 | ] |
0 | [ | |
1 | { | |
2 | "description": "boolean schema 'true'", | |
3 | "schema": true, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "number is valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "string is valid", | |
12 | "data": "foo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "boolean true is valid", | |
17 | "data": true, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "boolean false is valid", | |
22 | "data": false, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "null is valid", | |
27 | "data": null, | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "object is valid", | |
32 | "data": {"foo": "bar"}, | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "empty object is valid", | |
37 | "data": {}, | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "array is valid", | |
42 | "data": ["foo"], | |
43 | "valid": true | |
44 | }, | |
45 | { | |
46 | "description": "empty array is valid", | |
47 | "data": [], | |
48 | "valid": true | |
49 | } | |
50 | ] | |
51 | }, | |
52 | { | |
53 | "description": "boolean schema 'false'", | |
54 | "schema": false, | |
55 | "tests": [ | |
56 | { | |
57 | "description": "number is invalid", | |
58 | "data": 1, | |
59 | "valid": false | |
60 | }, | |
61 | { | |
62 | "description": "string is invalid", | |
63 | "data": "foo", | |
64 | "valid": false | |
65 | }, | |
66 | { | |
67 | "description": "boolean true is invalid", | |
68 | "data": true, | |
69 | "valid": false | |
70 | }, | |
71 | { | |
72 | "description": "boolean false is invalid", | |
73 | "data": false, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "null is invalid", | |
78 | "data": null, | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "object is invalid", | |
83 | "data": {"foo": "bar"}, | |
84 | "valid": false | |
85 | }, | |
86 | { | |
87 | "description": "empty object is invalid", | |
88 | "data": {}, | |
89 | "valid": false | |
90 | }, | |
91 | { | |
92 | "description": "array is invalid", | |
93 | "data": ["foo"], | |
94 | "valid": false | |
95 | }, | |
96 | { | |
97 | "description": "empty array is invalid", | |
98 | "data": [], | |
99 | "valid": false | |
100 | } | |
101 | ] | |
102 | } | |
103 | ] |
0 | [ | |
1 | { | |
2 | "description": "const validation", | |
3 | "schema": {"const": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "same value is valid", | |
7 | "data": 2, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "another value is invalid", | |
12 | "data": 5, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "another type is invalid", | |
17 | "data": "a", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "const with object", | |
24 | "schema": {"const": {"foo": "bar", "baz": "bax"}}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "same object is valid", | |
28 | "data": {"foo": "bar", "baz": "bax"}, | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "same object with different property order is valid", | |
33 | "data": {"baz": "bax", "foo": "bar"}, | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "another object is invalid", | |
38 | "data": {"foo": "bar"}, | |
39 | "valid": false | |
40 | }, | |
41 | { | |
42 | "description": "another type is invalid", | |
43 | "data": [1, 2], | |
44 | "valid": false | |
45 | } | |
46 | ] | |
47 | }, | |
48 | { | |
49 | "description": "const with array", | |
50 | "schema": {"const": [{ "foo": "bar" }]}, | |
51 | "tests": [ | |
52 | { | |
53 | "description": "same array is valid", | |
54 | "data": [{"foo": "bar"}], | |
55 | "valid": true | |
56 | }, | |
57 | { | |
58 | "description": "another array item is invalid", | |
59 | "data": [2], | |
60 | "valid": false | |
61 | }, | |
62 | { | |
63 | "description": "array with additional items is invalid", | |
64 | "data": [1, 2, 3], | |
65 | "valid": false | |
66 | } | |
67 | ] | |
68 | }, | |
69 | { | |
70 | "description": "const with null", | |
71 | "schema": {"const": null}, | |
72 | "tests": [ | |
73 | { | |
74 | "description": "null is valid", | |
75 | "data": null, | |
76 | "valid": true | |
77 | }, | |
78 | { | |
79 | "description": "not null is invalid", | |
80 | "data": 0, | |
81 | "valid": false | |
82 | } | |
83 | ] | |
84 | } | |
85 | ] |
0 | [ | |
1 | { | |
2 | "description": "contains keyword validation", | |
3 | "schema": { | |
4 | "contains": {"minimum": 5} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "array with item matching schema (5) is valid", | |
9 | "data": [3, 4, 5], | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "array with item matching schema (6) is valid", | |
14 | "data": [3, 4, 6], | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "array with two items matching schema (5, 6) is valid", | |
19 | "data": [3, 4, 5, 6], | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "array without items matching schema is invalid", | |
24 | "data": [2, 3, 4], | |
25 | "valid": false | |
26 | }, | |
27 | { | |
28 | "description": "empty array is invalid", | |
29 | "data": [], | |
30 | "valid": false | |
31 | }, | |
32 | { | |
33 | "description": "not array is valid", | |
34 | "data": {}, | |
35 | "valid": true | |
36 | } | |
37 | ] | |
38 | }, | |
39 | { | |
40 | "description": "contains keyword with const keyword", | |
41 | "schema": { | |
42 | "contains": { "const": 5 } | |
43 | }, | |
44 | "tests": [ | |
45 | { | |
46 | "description": "array with item 5 is valid", | |
47 | "data": [3, 4, 5], | |
48 | "valid": true | |
49 | }, | |
50 | { | |
51 | "description": "array with two items 5 is valid", | |
52 | "data": [3, 4, 5, 5], | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "array without item 5 is invalid", | |
57 | "data": [1, 2, 3, 4], | |
58 | "valid": false | |
59 | } | |
60 | ] | |
61 | }, | |
62 | { | |
63 | "description": "contains keyword with boolean schema true", | |
64 | "schema": {"contains": true}, | |
65 | "tests": [ | |
66 | { | |
67 | "description": "any non-empty array is valid", | |
68 | "data": ["foo"], | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "empty array is invalid", | |
73 | "data": [], | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "contains keyword with boolean schema false", | |
80 | "schema": {"contains": false}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any non-empty array is invalid", | |
84 | "data": ["foo"], | |
85 | "valid": false | |
86 | }, | |
87 | { | |
88 | "description": "empty array is invalid", | |
89 | "data": [], | |
90 | "valid": false | |
91 | } | |
92 | ] | |
93 | } | |
94 | ] |
0 | [ | |
1 | { | |
2 | "description": "invalid type for default", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": { | |
6 | "type": "integer", | |
7 | "default": [] | |
8 | } | |
9 | } | |
10 | }, | |
11 | "tests": [ | |
12 | { | |
13 | "description": "valid when property is specified", | |
14 | "data": {"foo": 13}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "still valid when the invalid default is used", | |
19 | "data": {}, | |
20 | "valid": true | |
21 | } | |
22 | ] | |
23 | }, | |
24 | { | |
25 | "description": "invalid string value for default", | |
26 | "schema": { | |
27 | "properties": { | |
28 | "bar": { | |
29 | "type": "string", | |
30 | "minLength": 4, | |
31 | "default": "bad" | |
32 | } | |
33 | } | |
34 | }, | |
35 | "tests": [ | |
36 | { | |
37 | "description": "valid when property is specified", | |
38 | "data": {"bar": "good"}, | |
39 | "valid": true | |
40 | }, | |
41 | { | |
42 | "description": "still valid when the invalid default is used", | |
43 | "data": {}, | |
44 | "valid": true | |
45 | } | |
46 | ] | |
47 | } | |
48 | ] |
0 | [ | |
1 | { | |
2 | "description": "valid definition", | |
3 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "valid definition schema", | |
7 | "data": { | |
8 | "definitions": { | |
9 | "foo": {"type": "integer"} | |
10 | } | |
11 | }, | |
12 | "valid": true | |
13 | } | |
14 | ] | |
15 | }, | |
16 | { | |
17 | "description": "invalid definition", | |
18 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "invalid definition schema", | |
22 | "data": { | |
23 | "definitions": { | |
24 | "foo": {"type": 1} | |
25 | } | |
26 | }, | |
27 | "valid": false | |
28 | } | |
29 | ] | |
30 | } | |
31 | ] |
0 | [ | |
1 | { | |
2 | "description": "dependencies", | |
3 | "schema": { | |
4 | "dependencies": {"bar": ["foo"]} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "neither", | |
9 | "data": {}, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "nondependant", | |
14 | "data": {"foo": 1}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "with dependency", | |
19 | "data": {"foo": 1, "bar": 2}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "missing dependency", | |
24 | "data": {"bar": 2}, | |
25 | "valid": false | |
26 | }, | |
27 | { | |
28 | "description": "ignores arrays", | |
29 | "data": ["bar"], | |
30 | "valid": true | |
31 | }, | |
32 | { | |
33 | "description": "ignores strings", | |
34 | "data": "foobar", | |
35 | "valid": true | |
36 | }, | |
37 | { | |
38 | "description": "ignores other non-objects", | |
39 | "data": 12, | |
40 | "valid": true | |
41 | } | |
42 | ] | |
43 | }, | |
44 | { | |
45 | "description": "dependencies with empty array", | |
46 | "schema": { | |
47 | "dependencies": {"bar": []} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "empty object", | |
52 | "data": {}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "object with one property", | |
57 | "data": {"bar": 2}, | |
58 | "valid": true | |
59 | } | |
60 | ] | |
61 | }, | |
62 | { | |
63 | "description": "multiple dependencies", | |
64 | "schema": { | |
65 | "dependencies": {"quux": ["foo", "bar"]} | |
66 | }, | |
67 | "tests": [ | |
68 | { | |
69 | "description": "neither", | |
70 | "data": {}, | |
71 | "valid": true | |
72 | }, | |
73 | { | |
74 | "description": "nondependants", | |
75 | "data": {"foo": 1, "bar": 2}, | |
76 | "valid": true | |
77 | }, | |
78 | { | |
79 | "description": "with dependencies", | |
80 | "data": {"foo": 1, "bar": 2, "quux": 3}, | |
81 | "valid": true | |
82 | }, | |
83 | { | |
84 | "description": "missing dependency", | |
85 | "data": {"foo": 1, "quux": 2}, | |
86 | "valid": false | |
87 | }, | |
88 | { | |
89 | "description": "missing other dependency", | |
90 | "data": {"bar": 1, "quux": 2}, | |
91 | "valid": false | |
92 | }, | |
93 | { | |
94 | "description": "missing both dependencies", | |
95 | "data": {"quux": 1}, | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "multiple dependencies subschema", | |
102 | "schema": { | |
103 | "dependencies": { | |
104 | "bar": { | |
105 | "properties": { | |
106 | "foo": {"type": "integer"}, | |
107 | "bar": {"type": "integer"} | |
108 | } | |
109 | } | |
110 | } | |
111 | }, | |
112 | "tests": [ | |
113 | { | |
114 | "description": "valid", | |
115 | "data": {"foo": 1, "bar": 2}, | |
116 | "valid": true | |
117 | }, | |
118 | { | |
119 | "description": "no dependency", | |
120 | "data": {"foo": "quux"}, | |
121 | "valid": true | |
122 | }, | |
123 | { | |
124 | "description": "wrong type", | |
125 | "data": {"foo": "quux", "bar": 2}, | |
126 | "valid": false | |
127 | }, | |
128 | { | |
129 | "description": "wrong type other", | |
130 | "data": {"foo": 2, "bar": "quux"}, | |
131 | "valid": false | |
132 | }, | |
133 | { | |
134 | "description": "wrong type both", | |
135 | "data": {"foo": "quux", "bar": "quux"}, | |
136 | "valid": false | |
137 | } | |
138 | ] | |
139 | }, | |
140 | { | |
141 | "description": "dependencies with boolean subschemas", | |
142 | "schema": { | |
143 | "dependencies": { | |
144 | "foo": true, | |
145 | "bar": false | |
146 | } | |
147 | }, | |
148 | "tests": [ | |
149 | { | |
150 | "description": "object with property having schema true is valid", | |
151 | "data": {"foo": 1}, | |
152 | "valid": true | |
153 | }, | |
154 | { | |
155 | "description": "object with property having schema false is invalid", | |
156 | "data": {"bar": 2}, | |
157 | "valid": false | |
158 | }, | |
159 | { | |
160 | "description": "object with both properties is invalid", | |
161 | "data": {"foo": 1, "bar": 2}, | |
162 | "valid": false | |
163 | }, | |
164 | { | |
165 | "description": "empty object is valid", | |
166 | "data": {}, | |
167 | "valid": true | |
168 | } | |
169 | ] | |
170 | } | |
171 | ] |
0 | [ | |
1 | { | |
2 | "description": "simple enum validation", | |
3 | "schema": {"enum": [1, 2, 3]}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "one of the enum is valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "something else is invalid", | |
12 | "data": 4, | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "heterogeneous enum validation", | |
19 | "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "one of the enum is valid", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "something else is invalid", | |
28 | "data": null, | |
29 | "valid": false | |
30 | }, | |
31 | { | |
32 | "description": "objects are deep compared", | |
33 | "data": {"foo": false}, | |
34 | "valid": false | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "enums in properties", | |
40 | "schema": { | |
41 | "type":"object", | |
42 | "properties": { | |
43 | "foo": {"enum":["foo"]}, | |
44 | "bar": {"enum":["bar"]} | |
45 | }, | |
46 | "required": ["bar"] | |
47 | }, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "both properties are valid", | |
51 | "data": {"foo":"foo", "bar":"bar"}, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "missing optional property is valid", | |
56 | "data": {"bar":"bar"}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "missing required property is invalid", | |
61 | "data": {"foo":"foo"}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "missing all properties is invalid", | |
66 | "data": {}, | |
67 | "valid": false | |
68 | } | |
69 | ] | |
70 | } | |
71 | ] |
0 | [ | |
1 | { | |
2 | "description": "exclusiveMaximum validation", | |
3 | "schema": { | |
4 | "exclusiveMaximum": 3.0 | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "below the exclusiveMaximum is valid", | |
9 | "data": 2.2, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "boundary point is invalid", | |
14 | "data": 3.0, | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "above the exclusiveMaximum is invalid", | |
19 | "data": 3.5, | |
20 | "valid": false | |
21 | }, | |
22 | { | |
23 | "description": "ignores non-numbers", | |
24 | "data": "x", | |
25 | "valid": true | |
26 | } | |
27 | ] | |
28 | } | |
29 | ] |
0 | [ | |
1 | { | |
2 | "description": "exclusiveMinimum validation", | |
3 | "schema": { | |
4 | "exclusiveMinimum": 1.1 | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "above the exclusiveMinimum is valid", | |
9 | "data": 1.2, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "boundary point is invalid", | |
14 | "data": 1.1, | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "below the exclusiveMinimum is invalid", | |
19 | "data": 0.6, | |
20 | "valid": false | |
21 | }, | |
22 | { | |
23 | "description": "ignores non-numbers", | |
24 | "data": "x", | |
25 | "valid": true | |
26 | } | |
27 | ] | |
28 | } | |
29 | ] |
0 | [ | |
1 | { | |
2 | "description": "a schema given for items", | |
3 | "schema": { | |
4 | "items": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "valid items", | |
9 | "data": [ 1, 2, 3 ], | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "wrong type of items", | |
14 | "data": [1, "x"], | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "ignores non-arrays", | |
19 | "data": {"foo" : "bar"}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "JavaScript pseudo-array is valid", | |
24 | "data": { | |
25 | "0": "invalid", | |
26 | "length": 1 | |
27 | }, | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "an array of schemas for items", | |
34 | "schema": { | |
35 | "items": [ | |
36 | {"type": "integer"}, | |
37 | {"type": "string"} | |
38 | ] | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "correct types", | |
43 | "data": [ 1, "foo" ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "wrong types", | |
48 | "data": [ "foo", 1 ], | |
49 | "valid": false | |
50 | }, | |
51 | { | |
52 | "description": "incomplete array of items", | |
53 | "data": [ 1 ], | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "array with additional items", | |
58 | "data": [ 1, "foo", true ], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "empty array", | |
63 | "data": [ ], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "JavaScript pseudo-array is valid", | |
68 | "data": { | |
69 | "0": "invalid", | |
70 | "1": "valid", | |
71 | "length": 2 | |
72 | }, | |
73 | "valid": true | |
74 | } | |
75 | ] | |
76 | }, | |
77 | { | |
78 | "description": "items with boolean schema (true)", | |
79 | "schema": {"items": true}, | |
80 | "tests": [ | |
81 | { | |
82 | "description": "any array is valid", | |
83 | "data": [ 1, "foo", true ], | |
84 | "valid": true | |
85 | }, | |
86 | { | |
87 | "description": "empty array is valid", | |
88 | "data": [], | |
89 | "valid": true | |
90 | } | |
91 | ] | |
92 | }, | |
93 | { | |
94 | "description": "items with boolean schema (false)", | |
95 | "schema": {"items": false}, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "any non-empty array is invalid", | |
99 | "data": [ 1, "foo", true ], | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "empty array is valid", | |
104 | "data": [], | |
105 | "valid": true | |
106 | } | |
107 | ] | |
108 | }, | |
109 | { | |
110 | "description": "items with boolean schemas", | |
111 | "schema": { | |
112 | "items": [true, false] | |
113 | }, | |
114 | "tests": [ | |
115 | { | |
116 | "description": "array with one item is valid", | |
117 | "data": [ 1 ], | |
118 | "valid": true | |
119 | }, | |
120 | { | |
121 | "description": "array with two items is invalid", | |
122 | "data": [ 1, "foo" ], | |
123 | "valid": false | |
124 | }, | |
125 | { | |
126 | "description": "empty array is valid", | |
127 | "data": [], | |
128 | "valid": true | |
129 | } | |
130 | ] | |
131 | } | |
132 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxItems validation", | |
3 | "schema": {"maxItems": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": [1], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1, 2], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": [1, 2, 3], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "foobar", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxLength validation", | |
3 | "schema": {"maxLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": "f", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 100, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "two supplementary Unicode code points is long enough", | |
27 | "data": "\uD83D\uDCA9\uD83D\uDCA9", | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxProperties validation", | |
3 | "schema": {"maxProperties": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": {"foo": 1}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1, "bar": 2}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": {"foo": 1, "bar": 2, "baz": 3}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobar", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "maximum validation", | |
3 | "schema": {"maximum": 3.0}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "below the maximum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 3.0, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "above the maximum is invalid", | |
17 | "data": 3.5, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "minItems validation", | |
3 | "schema": {"minItems": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": [], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "minLength validation", | |
3 | "schema": {"minLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": "foo", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": "f", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 1, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "one supplementary Unicode code point is not long enough", | |
27 | "data": "\uD83D\uDCA9", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "minProperties validation", | |
3 | "schema": {"minProperties": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": {"foo": 1, "bar": 2}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": {}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "minimum validation", | |
3 | "schema": {"minimum": 1.1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "above the minimum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 1.1, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "below the minimum is invalid", | |
17 | "data": 0.6, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "by int", | |
3 | "schema": {"multipleOf": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "int by int", | |
7 | "data": 10, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "int by int fail", | |
12 | "data": 7, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-numbers", | |
17 | "data": "foo", | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "by number", | |
24 | "schema": {"multipleOf": 1.5}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "zero is multiple of anything", | |
28 | "data": 0, | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "4.5 is multiple of 1.5", | |
33 | "data": 4.5, | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "35 is not multiple of 1.5", | |
38 | "data": 35, | |
39 | "valid": false | |
40 | } | |
41 | ] | |
42 | }, | |
43 | { | |
44 | "description": "by small number", | |
45 | "schema": {"multipleOf": 0.0001}, | |
46 | "tests": [ | |
47 | { | |
48 | "description": "0.0075 is multiple of 0.0001", | |
49 | "data": 0.0075, | |
50 | "valid": true | |
51 | }, | |
52 | { | |
53 | "description": "0.00751 is not multiple of 0.0001", | |
54 | "data": 0.00751, | |
55 | "valid": false | |
56 | } | |
57 | ] | |
58 | } | |
59 | ] |
0 | [ | |
1 | { | |
2 | "description": "not", | |
3 | "schema": { | |
4 | "not": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "allowed", | |
9 | "data": "foo", | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "disallowed", | |
14 | "data": 1, | |
15 | "valid": false | |
16 | } | |
17 | ] | |
18 | }, | |
19 | { | |
20 | "description": "not multiple types", | |
21 | "schema": { | |
22 | "not": {"type": ["integer", "boolean"]} | |
23 | }, | |
24 | "tests": [ | |
25 | { | |
26 | "description": "valid", | |
27 | "data": "foo", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "mismatch", | |
32 | "data": 1, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "other mismatch", | |
37 | "data": true, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "not more complex schema", | |
44 | "schema": { | |
45 | "not": { | |
46 | "type": "object", | |
47 | "properties": { | |
48 | "foo": { | |
49 | "type": "string" | |
50 | } | |
51 | } | |
52 | } | |
53 | }, | |
54 | "tests": [ | |
55 | { | |
56 | "description": "match", | |
57 | "data": 1, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "other match", | |
62 | "data": {"foo": 1}, | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "mismatch", | |
67 | "data": {"foo": "bar"}, | |
68 | "valid": false | |
69 | } | |
70 | ] | |
71 | }, | |
72 | { | |
73 | "description": "forbidden property", | |
74 | "schema": { | |
75 | "properties": { | |
76 | "foo": { | |
77 | "not": {} | |
78 | } | |
79 | } | |
80 | }, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "property present", | |
84 | "data": {"foo": 1, "bar": 2}, | |
85 | "valid": false | |
86 | }, | |
87 | { | |
88 | "description": "property absent", | |
89 | "data": {"bar": 1, "baz": 2}, | |
90 | "valid": true | |
91 | } | |
92 | ] | |
93 | }, | |
94 | { | |
95 | "description": "not with boolean schema true", | |
96 | "schema": {"not": true}, | |
97 | "tests": [ | |
98 | { | |
99 | "description": "any value is invalid", | |
100 | "data": "foo", | |
101 | "valid": false | |
102 | } | |
103 | ] | |
104 | }, | |
105 | { | |
106 | "description": "not with boolean schema false", | |
107 | "schema": {"not": false}, | |
108 | "tests": [ | |
109 | { | |
110 | "description": "any value is valid", | |
111 | "data": "foo", | |
112 | "valid": true | |
113 | } | |
114 | ] | |
115 | } | |
116 | ] |
0 | [ | |
1 | { | |
2 | "description": "oneOf", | |
3 | "schema": { | |
4 | "oneOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first oneOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second oneOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both oneOf valid", | |
26 | "data": 3, | |
27 | "valid": false | |
28 | }, | |
29 | { | |
30 | "description": "neither oneOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "oneOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "oneOf" : [ | |
41 | { | |
42 | "minLength": 2 | |
43 | }, | |
44 | { | |
45 | "maxLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one oneOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both oneOf valid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "oneOf with boolean schemas, all true", | |
69 | "schema": {"oneOf": [true, true, true]}, | |
70 | "tests": [ | |
71 | { | |
72 | "description": "any value is invalid", | |
73 | "data": "foo", | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "oneOf with boolean schemas, one true", | |
80 | "schema": {"oneOf": [true, false, false]}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any value is valid", | |
84 | "data": "foo", | |
85 | "valid": true | |
86 | } | |
87 | ] | |
88 | }, | |
89 | { | |
90 | "description": "oneOf with boolean schemas, more than one true", | |
91 | "schema": {"oneOf": [true, true, false]}, | |
92 | "tests": [ | |
93 | { | |
94 | "description": "any value is invalid", | |
95 | "data": "foo", | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "oneOf with boolean schemas, all false", | |
102 | "schema": {"oneOf": [false, false, false]}, | |
103 | "tests": [ | |
104 | { | |
105 | "description": "any value is invalid", | |
106 | "data": "foo", | |
107 | "valid": false | |
108 | } | |
109 | ] | |
110 | }, | |
111 | { | |
112 | "description": "oneOf complex types", | |
113 | "schema": { | |
114 | "oneOf": [ | |
115 | { | |
116 | "properties": { | |
117 | "bar": {"type": "integer"} | |
118 | }, | |
119 | "required": ["bar"] | |
120 | }, | |
121 | { | |
122 | "properties": { | |
123 | "foo": {"type": "string"} | |
124 | }, | |
125 | "required": ["foo"] | |
126 | } | |
127 | ] | |
128 | }, | |
129 | "tests": [ | |
130 | { | |
131 | "description": "first oneOf valid (complex)", | |
132 | "data": {"bar": 2}, | |
133 | "valid": true | |
134 | }, | |
135 | { | |
136 | "description": "second oneOf valid (complex)", | |
137 | "data": {"foo": "baz"}, | |
138 | "valid": true | |
139 | }, | |
140 | { | |
141 | "description": "both oneOf valid (complex)", | |
142 | "data": {"foo": "baz", "bar": 2}, | |
143 | "valid": false | |
144 | }, | |
145 | { | |
146 | "description": "neither oneOf valid (complex)", | |
147 | "data": {"foo": 2, "bar": "quux"}, | |
148 | "valid": false | |
149 | } | |
150 | ] | |
151 | } | |
152 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a bignum is an integer", | |
7 | "data": 12345678910111213141516171819202122232425262728293031, | |
8 | "valid": true | |
9 | } | |
10 | ] | |
11 | }, | |
12 | { | |
13 | "description": "number", | |
14 | "schema": {"type": "number"}, | |
15 | "tests": [ | |
16 | { | |
17 | "description": "a bignum is a number", | |
18 | "data": 98249283749234923498293171823948729348710298301928331, | |
19 | "valid": true | |
20 | } | |
21 | ] | |
22 | }, | |
23 | { | |
24 | "description": "integer", | |
25 | "schema": {"type": "integer"}, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "a negative bignum is an integer", | |
29 | "data": -12345678910111213141516171819202122232425262728293031, | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "number", | |
36 | "schema": {"type": "number"}, | |
37 | "tests": [ | |
38 | { | |
39 | "description": "a negative bignum is a number", | |
40 | "data": -98249283749234923498293171823948729348710298301928331, | |
41 | "valid": true | |
42 | } | |
43 | ] | |
44 | }, | |
45 | { | |
46 | "description": "string", | |
47 | "schema": {"type": "string"}, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "a bignum is not a string", | |
51 | "data": 98249283749234923498293171823948729348710298301928331, | |
52 | "valid": false | |
53 | } | |
54 | ] | |
55 | }, | |
56 | { | |
57 | "description": "integer comparison", | |
58 | "schema": {"maximum": 18446744073709551615}, | |
59 | "tests": [ | |
60 | { | |
61 | "description": "comparison works for high numbers", | |
62 | "data": 18446744073709551600, | |
63 | "valid": true | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "float comparison with high precision", | |
69 | "schema": { | |
70 | "exclusiveMaximum": 972783798187987123879878123.18878137 | |
71 | }, | |
72 | "tests": [ | |
73 | { | |
74 | "description": "comparison works for high numbers", | |
75 | "data": 972783798187987123879878123.188781371, | |
76 | "valid": false | |
77 | } | |
78 | ] | |
79 | }, | |
80 | { | |
81 | "description": "integer comparison", | |
82 | "schema": {"minimum": -18446744073709551615}, | |
83 | "tests": [ | |
84 | { | |
85 | "description": "comparison works for very negative numbers", | |
86 | "data": -18446744073709551600, | |
87 | "valid": true | |
88 | } | |
89 | ] | |
90 | }, | |
91 | { | |
92 | "description": "float comparison with high precision on negative numbers", | |
93 | "schema": { | |
94 | "exclusiveMinimum": -972783798187987123879878123.18878137 | |
95 | }, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "comparison works for very negative numbers", | |
99 | "data": -972783798187987123879878123.188781371, | |
100 | "valid": false | |
101 | } | |
102 | ] | |
103 | } | |
104 | ] |
0 | [ | |
1 | { | |
2 | "description": "ECMA 262 regex non-compliance", | |
3 | "schema": { "format": "regex" }, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "ECMA 262 has no support for \\Z anchor from .NET", | |
7 | "data": "^\\S(|(.|\\n)*\\S)\\Z", | |
8 | "valid": false | |
9 | } | |
10 | ] | |
11 | } | |
12 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of date-time strings", | |
3 | "schema": {"format": "date-time"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid date-time string", | |
7 | "data": "1963-06-19T08:30:06.283185Z", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid date-time string", | |
12 | "data": "06/19/1963 08:30:06 PST", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "only RFC3339 not all of ISO 8601 are valid", | |
17 | "data": "2013-350T01:01:01", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "validation of URIs", | |
24 | "schema": {"format": "uri"}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "a valid URL with anchor tag", | |
28 | "data": "http://foo.bar/?baz=qux#quux", | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "a valid URL with anchor tag and parantheses", | |
33 | "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "a valid URL with URL-encoded stuff", | |
38 | "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", | |
39 | "valid": true | |
40 | }, | |
41 | { | |
42 | "description": "a valid puny-coded URL ", | |
43 | "data": "http://xn--nw2a.xn--j6w193g/", | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "a valid URL with many special characters", | |
48 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "a valid URL based on IPv4", | |
53 | "data": "http://223.255.255.254", | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "a valid URL with ftp scheme", | |
58 | "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a valid URL for a simple text file", | |
63 | "data": "http://www.ietf.org/rfc/rfc2396.txt", | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "a valid URL ", | |
68 | "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "a valid mailto URI", | |
73 | "data": "mailto:John.Doe@example.com", | |
74 | "valid": true | |
75 | }, | |
76 | { | |
77 | "description": "a valid newsgroup URI", | |
78 | "data": "news:comp.infosystems.www.servers.unix", | |
79 | "valid": true | |
80 | }, | |
81 | { | |
82 | "description": "a valid tel URI", | |
83 | "data": "tel:+1-816-555-1212", | |
84 | "valid": true | |
85 | }, | |
86 | { | |
87 | "description": "a valid URN", | |
88 | "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", | |
89 | "valid": true | |
90 | }, | |
91 | { | |
92 | "description": "an invalid protocol-relative URI Reference", | |
93 | "data": "//foo.bar/?baz=qux#quux", | |
94 | "valid": false | |
95 | }, | |
96 | { | |
97 | "description": "an invalid relative URI Reference", | |
98 | "data": "/abc", | |
99 | "valid": false | |
100 | }, | |
101 | { | |
102 | "description": "an invalid URI", | |
103 | "data": "\\\\WINDOWS\\fileshare", | |
104 | "valid": false | |
105 | }, | |
106 | { | |
107 | "description": "an invalid URI though valid URI reference", | |
108 | "data": "abc", | |
109 | "valid": false | |
110 | }, | |
111 | { | |
112 | "description": "an invalid URI with spaces", | |
113 | "data": "http:// shouldfail.com", | |
114 | "valid": false | |
115 | }, | |
116 | { | |
117 | "description": "an invalid URI with spaces and missing scheme", | |
118 | "data": ":// should fail", | |
119 | "valid": false | |
120 | } | |
121 | ] | |
122 | }, | |
123 | { | |
124 | "description": "validation of URI References", | |
125 | "schema": {"format": "uri-reference"}, | |
126 | "tests": [ | |
127 | { | |
128 | "description": "a valid URI", | |
129 | "data": "http://foo.bar/?baz=qux#quux", | |
130 | "valid": true | |
131 | }, | |
132 | { | |
133 | "description": "a valid protocol-relative URI Reference", | |
134 | "data": "//foo.bar/?baz=qux#quux", | |
135 | "valid": true | |
136 | }, | |
137 | { | |
138 | "description": "a valid relative URI Reference", | |
139 | "data": "/abc", | |
140 | "valid": true | |
141 | }, | |
142 | { | |
143 | "description": "an invalid URI Reference", | |
144 | "data": "\\\\WINDOWS\\fileshare", | |
145 | "valid": false | |
146 | }, | |
147 | { | |
148 | "description": "a valid URI Reference", | |
149 | "data": "abc", | |
150 | "valid": true | |
151 | }, | |
152 | { | |
153 | "description": "a valid URI fragment", | |
154 | "data": "#fragment", | |
155 | "valid": true | |
156 | }, | |
157 | { | |
158 | "description": "an invalid URI fragment", | |
159 | "data": "#frag\\ment", | |
160 | "valid": false | |
161 | } | |
162 | ] | |
163 | }, | |
164 | { | |
165 | "description": "format: uri-template", | |
166 | "schema": { | |
167 | "format": "uri-template" | |
168 | }, | |
169 | "tests": [ | |
170 | { | |
171 | "description": "a valid uri-template", | |
172 | "data": "http://example.com/dictionary/{term:1}/{term}", | |
173 | "valid": true | |
174 | }, | |
175 | { | |
176 | "description": "an invalid uri-template", | |
177 | "data": "http://example.com/dictionary/{term:1}/{term", | |
178 | "valid": false | |
179 | }, | |
180 | { | |
181 | "description": "a valid uri-template without variables", | |
182 | "data": "http://example.com/dictionary", | |
183 | "valid": true | |
184 | }, | |
185 | { | |
186 | "description": "a valid relative uri-template", | |
187 | "data": "dictionary/{term:1}/{term}", | |
188 | "valid": true | |
189 | } | |
190 | ] | |
191 | }, | |
192 | { | |
193 | "description": "validation of e-mail addresses", | |
194 | "schema": {"format": "email"}, | |
195 | "tests": [ | |
196 | { | |
197 | "description": "a valid e-mail address", | |
198 | "data": "joe.bloggs@example.com", | |
199 | "valid": true | |
200 | }, | |
201 | { | |
202 | "description": "an invalid e-mail address", | |
203 | "data": "2962", | |
204 | "valid": false | |
205 | } | |
206 | ] | |
207 | }, | |
208 | { | |
209 | "description": "validation of IP addresses", | |
210 | "schema": {"format": "ipv4"}, | |
211 | "tests": [ | |
212 | { | |
213 | "description": "a valid IP address", | |
214 | "data": "192.168.0.1", | |
215 | "valid": true | |
216 | }, | |
217 | { | |
218 | "description": "an IP address with too many components", | |
219 | "data": "127.0.0.0.1", | |
220 | "valid": false | |
221 | }, | |
222 | { | |
223 | "description": "an IP address with out-of-range values", | |
224 | "data": "256.256.256.256", | |
225 | "valid": false | |
226 | }, | |
227 | { | |
228 | "description": "an IP address without 4 components", | |
229 | "data": "127.0", | |
230 | "valid": false | |
231 | }, | |
232 | { | |
233 | "description": "an IP address as an integer", | |
234 | "data": "0x7f000001", | |
235 | "valid": false | |
236 | } | |
237 | ] | |
238 | }, | |
239 | { | |
240 | "description": "validation of IPv6 addresses", | |
241 | "schema": {"format": "ipv6"}, | |
242 | "tests": [ | |
243 | { | |
244 | "description": "a valid IPv6 address", | |
245 | "data": "::1", | |
246 | "valid": true | |
247 | }, | |
248 | { | |
249 | "description": "an IPv6 address with out-of-range values", | |
250 | "data": "12345::", | |
251 | "valid": false | |
252 | }, | |
253 | { | |
254 | "description": "an IPv6 address with too many components", | |
255 | "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", | |
256 | "valid": false | |
257 | }, | |
258 | { | |
259 | "description": "an IPv6 address containing illegal characters", | |
260 | "data": "::laptop", | |
261 | "valid": false | |
262 | } | |
263 | ] | |
264 | }, | |
265 | { | |
266 | "description": "validation of host names", | |
267 | "schema": {"format": "hostname"}, | |
268 | "tests": [ | |
269 | { | |
270 | "description": "a valid host name", | |
271 | "data": "www.example.com", | |
272 | "valid": true | |
273 | }, | |
274 | { | |
275 | "description": "a host name starting with an illegal character", | |
276 | "data": "-a-host-name-that-starts-with--", | |
277 | "valid": false | |
278 | }, | |
279 | { | |
280 | "description": "a host name containing illegal characters", | |
281 | "data": "not_a_valid_host_name", | |
282 | "valid": false | |
283 | }, | |
284 | { | |
285 | "description": "a host name with a component too long", | |
286 | "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", | |
287 | "valid": false | |
288 | } | |
289 | ] | |
290 | }, | |
291 | { | |
292 | "description": "validation of JSON-pointers (JSON String Representation)", | |
293 | "schema": {"format": "json-pointer"}, | |
294 | "tests": [ | |
295 | { | |
296 | "description": "a valid JSON-pointer", | |
297 | "data": "/foo/bar~0/baz~1/%a", | |
298 | "valid": true | |
299 | }, | |
300 | { | |
301 | "description": "not a valid JSON-pointer (~ not escaped)", | |
302 | "data": "/foo/bar~", | |
303 | "valid": false | |
304 | }, | |
305 | { | |
306 | "description": "valid JSON-pointer with empty segment", | |
307 | "data": "/foo//bar", | |
308 | "valid": true | |
309 | }, | |
310 | { | |
311 | "description": "valid JSON-pointer with the last empty segment", | |
312 | "data": "/foo/bar/", | |
313 | "valid": true | |
314 | }, | |
315 | { | |
316 | "description": "valid JSON-pointer as stated in RFC 6901 #1", | |
317 | "data": "", | |
318 | "valid": true | |
319 | }, | |
320 | { | |
321 | "description": "valid JSON-pointer as stated in RFC 6901 #2", | |
322 | "data": "/foo", | |
323 | "valid": true | |
324 | }, | |
325 | { | |
326 | "description": "valid JSON-pointer as stated in RFC 6901 #3", | |
327 | "data": "/foo/0", | |
328 | "valid": true | |
329 | }, | |
330 | { | |
331 | "description": "valid JSON-pointer as stated in RFC 6901 #4", | |
332 | "data": "/", | |
333 | "valid": true | |
334 | }, | |
335 | { | |
336 | "description": "valid JSON-pointer as stated in RFC 6901 #5", | |
337 | "data": "/a~1b", | |
338 | "valid": true | |
339 | }, | |
340 | { | |
341 | "description": "valid JSON-pointer as stated in RFC 6901 #6", | |
342 | "data": "/c%d", | |
343 | "valid": true | |
344 | }, | |
345 | { | |
346 | "description": "valid JSON-pointer as stated in RFC 6901 #7", | |
347 | "data": "/e^f", | |
348 | "valid": true | |
349 | }, | |
350 | { | |
351 | "description": "valid JSON-pointer as stated in RFC 6901 #8", | |
352 | "data": "/g|h", | |
353 | "valid": true | |
354 | }, | |
355 | { | |
356 | "description": "valid JSON-pointer as stated in RFC 6901 #9", | |
357 | "data": "/i\\j", | |
358 | "valid": true | |
359 | }, | |
360 | { | |
361 | "description": "valid JSON-pointer as stated in RFC 6901 #10", | |
362 | "data": "/k\"l", | |
363 | "valid": true | |
364 | }, | |
365 | { | |
366 | "description": "valid JSON-pointer as stated in RFC 6901 #11", | |
367 | "data": "/ ", | |
368 | "valid": true | |
369 | }, | |
370 | { | |
371 | "description": "valid JSON-pointer as stated in RFC 6901 #12", | |
372 | "data": "/m~0n", | |
373 | "valid": true | |
374 | }, | |
375 | { | |
376 | "description": "valid JSON-pointer used adding to the last array position", | |
377 | "data": "/foo/-", | |
378 | "valid": true | |
379 | }, | |
380 | { | |
381 | "description": "valid JSON-pointer (- used as object member name)", | |
382 | "data": "/foo/-/bar", | |
383 | "valid": true | |
384 | }, | |
385 | { | |
386 | "description": "valid JSON-pointer (multiple escaped characters)", | |
387 | "data": "/~1~0~0~1~1", | |
388 | "valid": true | |
389 | }, | |
390 | { | |
391 | "description": "valid JSON-pointer (escaped with fraction part) #1", | |
392 | "data": "/~1.1", | |
393 | "valid": true | |
394 | }, | |
395 | { | |
396 | "description": "valid JSON-pointer (escaped with fraction part) #2", | |
397 | "data": "/~0.1", | |
398 | "valid": true | |
399 | }, | |
400 | { | |
401 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", | |
402 | "data": "#", | |
403 | "valid": false | |
404 | }, | |
405 | { | |
406 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", | |
407 | "data": "#/", | |
408 | "valid": false | |
409 | }, | |
410 | { | |
411 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", | |
412 | "data": "#a", | |
413 | "valid": false | |
414 | }, | |
415 | { | |
416 | "description": "not a valid JSON-pointer (some escaped, but not all) #1", | |
417 | "data": "/~0~", | |
418 | "valid": false | |
419 | }, | |
420 | { | |
421 | "description": "not a valid JSON-pointer (some escaped, but not all) #2", | |
422 | "data": "/~0/~", | |
423 | "valid": false | |
424 | }, | |
425 | { | |
426 | "description": "not a valid JSON-pointer (wrong escape character) #1", | |
427 | "data": "/~2", | |
428 | "valid": false | |
429 | }, | |
430 | { | |
431 | "description": "not a valid JSON-pointer (wrong escape character) #2", | |
432 | "data": "/~-1", | |
433 | "valid": false | |
434 | }, | |
435 | { | |
436 | "description": "not a valid JSON-pointer (multiple characters not escaped)", | |
437 | "data": "/~~", | |
438 | "valid": false | |
439 | }, | |
440 | { | |
441 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", | |
442 | "data": "a", | |
443 | "valid": false | |
444 | }, | |
445 | { | |
446 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", | |
447 | "data": "0", | |
448 | "valid": false | |
449 | }, | |
450 | { | |
451 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", | |
452 | "data": "a/a", | |
453 | "valid": false | |
454 | } | |
455 | ] | |
456 | } | |
457 | ] |
0 | [ | |
1 | { | |
2 | "description": "some languages do not distinguish between different types of numeric value", | |
3 | "schema": { | |
4 | "type": "integer" | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "a float without fractional part is an integer", | |
9 | "data": 1.0, | |
10 | "valid": true | |
11 | } | |
12 | ] | |
13 | } | |
14 | ] |
0 | [ | |
1 | { | |
2 | "description": "pattern validation", | |
3 | "schema": {"pattern": "^a*$"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a matching pattern is valid", | |
7 | "data": "aaa", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a non-matching pattern is invalid", | |
12 | "data": "abc", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-strings", | |
17 | "data": true, | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "pattern is not anchored", | |
24 | "schema": {"pattern": "a+"}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "matches a substring", | |
28 | "data": "xxaayy", | |
29 | "valid": true | |
30 | } | |
31 | ] | |
32 | } | |
33 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "patternProperties validates properties matching a regex", | |
4 | "schema": { | |
5 | "patternProperties": { | |
6 | "f.*o": {"type": "integer"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "a single valid match is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "multiple valid matches is valid", | |
17 | "data": {"foo": 1, "foooooo" : 2}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a single invalid match is invalid", | |
22 | "data": {"foo": "bar", "fooooo": 2}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "multiple invalid matches is invalid", | |
27 | "data": {"foo": "bar", "foooooo" : "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": ["foo"], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores strings", | |
37 | "data": "foo", | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "ignores other non-objects", | |
42 | "data": 12, | |
43 | "valid": true | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "multiple simultaneous patternProperties are validated", | |
49 | "schema": { | |
50 | "patternProperties": { | |
51 | "a*": {"type": "integer"}, | |
52 | "aaa*": {"maximum": 20} | |
53 | } | |
54 | }, | |
55 | "tests": [ | |
56 | { | |
57 | "description": "a single valid match is valid", | |
58 | "data": {"a": 21}, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a simultaneous match is valid", | |
63 | "data": {"aaaa": 18}, | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "multiple matches is valid", | |
68 | "data": {"a": 21, "aaaa": 18}, | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "an invalid due to one is invalid", | |
73 | "data": {"a": "bar"}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an invalid due to the other is invalid", | |
78 | "data": {"aaaa": 31}, | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "an invalid due to both is invalid", | |
83 | "data": {"aaa": "foo", "aaaa": 31}, | |
84 | "valid": false | |
85 | } | |
86 | ] | |
87 | }, | |
88 | { | |
89 | "description": "regexes are not anchored by default and are case sensitive", | |
90 | "schema": { | |
91 | "patternProperties": { | |
92 | "[0-9]{2,}": { "type": "boolean" }, | |
93 | "X_": { "type": "string" } | |
94 | } | |
95 | }, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "non recognized members are ignored", | |
99 | "data": { "answer 1": "42" }, | |
100 | "valid": true | |
101 | }, | |
102 | { | |
103 | "description": "recognized members are accounted for", | |
104 | "data": { "a31b": null }, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "regexes are case sensitive", | |
109 | "data": { "a_x_3": 3 }, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "regexes are case sensitive, 2", | |
114 | "data": { "a_X_3": 3 }, | |
115 | "valid": false | |
116 | } | |
117 | ] | |
118 | }, | |
119 | { | |
120 | "description": "patternProperties with boolean schemas", | |
121 | "schema": { | |
122 | "patternProperties": { | |
123 | "f.*": true, | |
124 | "b.*": false | |
125 | } | |
126 | }, | |
127 | "tests": [ | |
128 | { | |
129 | "description": "object with property matching schema true is valid", | |
130 | "data": {"foo": 1}, | |
131 | "valid": true | |
132 | }, | |
133 | { | |
134 | "description": "object with property matching schema false is invalid", | |
135 | "data": {"bar": 2}, | |
136 | "valid": false | |
137 | }, | |
138 | { | |
139 | "description": "object with both properties is invalid", | |
140 | "data": {"foo": 1, "bar": 2}, | |
141 | "valid": false | |
142 | }, | |
143 | { | |
144 | "description": "empty object is valid", | |
145 | "data": {}, | |
146 | "valid": true | |
147 | } | |
148 | ] | |
149 | } | |
150 | ] |
0 | [ | |
1 | { | |
2 | "description": "object properties validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"type": "integer"}, | |
6 | "bar": {"type": "string"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "both properties present and valid is valid", | |
12 | "data": {"foo": 1, "bar": "baz"}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "one property invalid is invalid", | |
17 | "data": {"foo": 1, "bar": {}}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "both properties invalid is invalid", | |
22 | "data": {"foo": [], "bar": {}}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "doesn't invalidate other properties", | |
27 | "data": {"quux": []}, | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": [], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores other non-objects", | |
37 | "data": 12, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "properties, patternProperties, additionalProperties interaction", | |
45 | "schema": { | |
46 | "properties": { | |
47 | "foo": {"type": "array", "maxItems": 3}, | |
48 | "bar": {"type": "array"} | |
49 | }, | |
50 | "patternProperties": {"f.o": {"minItems": 2}}, | |
51 | "additionalProperties": {"type": "integer"} | |
52 | }, | |
53 | "tests": [ | |
54 | { | |
55 | "description": "property validates property", | |
56 | "data": {"foo": [1, 2]}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "property invalidates property", | |
61 | "data": {"foo": [1, 2, 3, 4]}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "patternProperty invalidates property", | |
66 | "data": {"foo": []}, | |
67 | "valid": false | |
68 | }, | |
69 | { | |
70 | "description": "patternProperty validates nonproperty", | |
71 | "data": {"fxo": [1, 2]}, | |
72 | "valid": true | |
73 | }, | |
74 | { | |
75 | "description": "patternProperty invalidates nonproperty", | |
76 | "data": {"fxo": []}, | |
77 | "valid": false | |
78 | }, | |
79 | { | |
80 | "description": "additionalProperty ignores property", | |
81 | "data": {"bar": []}, | |
82 | "valid": true | |
83 | }, | |
84 | { | |
85 | "description": "additionalProperty validates others", | |
86 | "data": {"quux": 3}, | |
87 | "valid": true | |
88 | }, | |
89 | { | |
90 | "description": "additionalProperty invalidates others", | |
91 | "data": {"quux": "foo"}, | |
92 | "valid": false | |
93 | } | |
94 | ] | |
95 | }, | |
96 | { | |
97 | "description": "properties with boolean schema", | |
98 | "schema": { | |
99 | "properties": { | |
100 | "foo": true, | |
101 | "bar": false | |
102 | } | |
103 | }, | |
104 | "tests": [ | |
105 | { | |
106 | "description": "no property present is valid", | |
107 | "data": {}, | |
108 | "valid": true | |
109 | }, | |
110 | { | |
111 | "description": "only 'true' property present is valid", | |
112 | "data": {"foo": 1}, | |
113 | "valid": true | |
114 | }, | |
115 | { | |
116 | "description": "only 'false' property present is invalid", | |
117 | "data": {"bar": 2}, | |
118 | "valid": false | |
119 | }, | |
120 | { | |
121 | "description": "both properties present is invalid", | |
122 | "data": {"foo": 1, "bar": 2}, | |
123 | "valid": false | |
124 | } | |
125 | ] | |
126 | } | |
127 | ] |
0 | [ | |
1 | { | |
2 | "description": "propertyNames validation", | |
3 | "schema": { | |
4 | "propertyNames": {"maxLength": 3} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "all property names valid", | |
9 | "data": { | |
10 | "f": {}, | |
11 | "foo": {} | |
12 | }, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "some property names invalid", | |
17 | "data": { | |
18 | "foo": {}, | |
19 | "foobar": {} | |
20 | }, | |
21 | "valid": false | |
22 | }, | |
23 | { | |
24 | "description": "object without properties is valid", | |
25 | "data": {}, | |
26 | "valid": true | |
27 | }, | |
28 | { | |
29 | "description": "ignores arrays", | |
30 | "data": [1, 2, 3, 4], | |
31 | "valid": true | |
32 | }, | |
33 | { | |
34 | "description": "ignores strings", | |
35 | "data": "foobar", | |
36 | "valid": true | |
37 | }, | |
38 | { | |
39 | "description": "ignores other non-objects", | |
40 | "data": 12, | |
41 | "valid": true | |
42 | } | |
43 | ] | |
44 | }, | |
45 | { | |
46 | "description": "propertyNames with boolean schema true", | |
47 | "schema": {"propertyNames": true}, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "object with any properties is valid", | |
51 | "data": {"foo": 1}, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "empty object is valid", | |
56 | "data": {}, | |
57 | "valid": true | |
58 | } | |
59 | ] | |
60 | }, | |
61 | { | |
62 | "description": "propertyNames with boolean schema false", | |
63 | "schema": {"propertyNames": false}, | |
64 | "tests": [ | |
65 | { | |
66 | "description": "object with any properties is invalid", | |
67 | "data": {"foo": 1}, | |
68 | "valid": false | |
69 | }, | |
70 | { | |
71 | "description": "empty object is valid", | |
72 | "data": {}, | |
73 | "valid": true | |
74 | } | |
75 | ] | |
76 | } | |
77 | ] |
0 | [ | |
1 | { | |
2 | "description": "root pointer ref", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"$ref": "#"} | |
6 | }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "match", | |
12 | "data": {"foo": false}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "recursive match", | |
17 | "data": {"foo": {"foo": false}}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "mismatch", | |
22 | "data": {"bar": false}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "recursive mismatch", | |
27 | "data": {"foo": {"bar": false}}, | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "relative pointer ref to object", | |
34 | "schema": { | |
35 | "properties": { | |
36 | "foo": {"type": "integer"}, | |
37 | "bar": {"$ref": "#/properties/foo"} | |
38 | } | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "match", | |
43 | "data": {"bar": 3}, | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "mismatch", | |
48 | "data": {"bar": true}, | |
49 | "valid": false | |
50 | } | |
51 | ] | |
52 | }, | |
53 | { | |
54 | "description": "relative pointer ref to array", | |
55 | "schema": { | |
56 | "items": [ | |
57 | {"type": "integer"}, | |
58 | {"$ref": "#/items/0"} | |
59 | ] | |
60 | }, | |
61 | "tests": [ | |
62 | { | |
63 | "description": "match array", | |
64 | "data": [1, 2], | |
65 | "valid": true | |
66 | }, | |
67 | { | |
68 | "description": "mismatch array", | |
69 | "data": [1, "foo"], | |
70 | "valid": false | |
71 | } | |
72 | ] | |
73 | }, | |
74 | { | |
75 | "description": "escaped pointer ref", | |
76 | "schema": { | |
77 | "tilda~field": {"type": "integer"}, | |
78 | "slash/field": {"type": "integer"}, | |
79 | "percent%field": {"type": "integer"}, | |
80 | "properties": { | |
81 | "tilda": {"$ref": "#/tilda~0field"}, | |
82 | "slash": {"$ref": "#/slash~1field"}, | |
83 | "percent": {"$ref": "#/percent%25field"} | |
84 | } | |
85 | }, | |
86 | "tests": [ | |
87 | { | |
88 | "description": "slash invalid", | |
89 | "data": {"slash": "aoeu"}, | |
90 | "valid": false | |
91 | }, | |
92 | { | |
93 | "description": "tilda invalid", | |
94 | "data": {"tilda": "aoeu"}, | |
95 | "valid": false | |
96 | }, | |
97 | { | |
98 | "description": "percent invalid", | |
99 | "data": {"percent": "aoeu"}, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "slash valid", | |
104 | "data": {"slash": 123}, | |
105 | "valid": true | |
106 | }, | |
107 | { | |
108 | "description": "tilda valid", | |
109 | "data": {"tilda": 123}, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "percent valid", | |
114 | "data": {"percent": 123}, | |
115 | "valid": true | |
116 | } | |
117 | ] | |
118 | }, | |
119 | { | |
120 | "description": "nested refs", | |
121 | "schema": { | |
122 | "definitions": { | |
123 | "a": {"type": "integer"}, | |
124 | "b": {"$ref": "#/definitions/a"}, | |
125 | "c": {"$ref": "#/definitions/b"} | |
126 | }, | |
127 | "$ref": "#/definitions/c" | |
128 | }, | |
129 | "tests": [ | |
130 | { | |
131 | "description": "nested ref valid", | |
132 | "data": 5, | |
133 | "valid": true | |
134 | }, | |
135 | { | |
136 | "description": "nested ref invalid", | |
137 | "data": "a", | |
138 | "valid": false | |
139 | } | |
140 | ] | |
141 | }, | |
142 | { | |
143 | "description": "ref overrides any sibling keywords", | |
144 | "schema": { | |
145 | "definitions": { | |
146 | "reffed": { | |
147 | "type": "array" | |
148 | } | |
149 | }, | |
150 | "properties": { | |
151 | "foo": { | |
152 | "$ref": "#/definitions/reffed", | |
153 | "maxItems": 2 | |
154 | } | |
155 | } | |
156 | }, | |
157 | "tests": [ | |
158 | { | |
159 | "description": "ref valid", | |
160 | "data": { "foo": [] }, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "ref valid, maxItems ignored", | |
165 | "data": { "foo": [ 1, 2, 3] }, | |
166 | "valid": true | |
167 | }, | |
168 | { | |
169 | "description": "ref invalid", | |
170 | "data": { "foo": "string" }, | |
171 | "valid": false | |
172 | } | |
173 | ] | |
174 | }, | |
175 | { | |
176 | "description": "remote ref, containing refs itself", | |
177 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, | |
178 | "tests": [ | |
179 | { | |
180 | "description": "remote ref valid", | |
181 | "data": {"minLength": 1}, | |
182 | "valid": true | |
183 | }, | |
184 | { | |
185 | "description": "remote ref invalid", | |
186 | "data": {"minLength": -1}, | |
187 | "valid": false | |
188 | } | |
189 | ] | |
190 | }, | |
191 | { | |
192 | "description": "property named $ref that is not a reference", | |
193 | "schema": { | |
194 | "properties": { | |
195 | "$ref": {"type": "string"} | |
196 | } | |
197 | }, | |
198 | "tests": [ | |
199 | { | |
200 | "description": "property named $ref valid", | |
201 | "data": {"$ref": "a"}, | |
202 | "valid": true | |
203 | }, | |
204 | { | |
205 | "description": "property named $ref invalid", | |
206 | "data": {"$ref": 2}, | |
207 | "valid": false | |
208 | } | |
209 | ] | |
210 | }, | |
211 | { | |
212 | "description": "$ref to boolean schema true", | |
213 | "schema": { | |
214 | "$ref": "#/definitions/bool", | |
215 | "definitions": { | |
216 | "bool": true | |
217 | } | |
218 | }, | |
219 | "tests": [ | |
220 | { | |
221 | "description": "any value is valid", | |
222 | "data": "foo", | |
223 | "valid": true | |
224 | } | |
225 | ] | |
226 | }, | |
227 | { | |
228 | "description": "$ref to boolean schema false", | |
229 | "schema": { | |
230 | "$ref": "#/definitions/bool", | |
231 | "definitions": { | |
232 | "bool": false | |
233 | } | |
234 | }, | |
235 | "tests": [ | |
236 | { | |
237 | "description": "any value is invalid", | |
238 | "data": "foo", | |
239 | "valid": false | |
240 | } | |
241 | ] | |
242 | }, | |
243 | { | |
244 | "description": "Recursive references between schemas", | |
245 | "schema": { | |
246 | "$id": "http://localhost:1234/tree", | |
247 | "description": "tree of nodes", | |
248 | "type": "object", | |
249 | "properties": { | |
250 | "meta": {"type": "string"}, | |
251 | "nodes": { | |
252 | "type": "array", | |
253 | "items": {"$ref": "node"} | |
254 | } | |
255 | }, | |
256 | "required": ["meta", "nodes"], | |
257 | "definitions": { | |
258 | "node": { | |
259 | "$id": "http://localhost:1234/node", | |
260 | "description": "node", | |
261 | "type": "object", | |
262 | "properties": { | |
263 | "value": {"type": "number"}, | |
264 | "subtree": {"$ref": "tree"} | |
265 | }, | |
266 | "required": ["value"] | |
267 | } | |
268 | } | |
269 | }, | |
270 | "tests": [ | |
271 | { | |
272 | "description": "valid tree", | |
273 | "data": { | |
274 | "meta": "root", | |
275 | "nodes": [ | |
276 | { | |
277 | "value": 1, | |
278 | "subtree": { | |
279 | "meta": "child", | |
280 | "nodes": [ | |
281 | {"value": 1.1}, | |
282 | {"value": 1.2} | |
283 | ] | |
284 | } | |
285 | }, | |
286 | { | |
287 | "value": 2, | |
288 | "subtree": { | |
289 | "meta": "child", | |
290 | "nodes": [ | |
291 | {"value": 2.1}, | |
292 | {"value": 2.2} | |
293 | ] | |
294 | } | |
295 | } | |
296 | ] | |
297 | }, | |
298 | "valid": true | |
299 | }, | |
300 | { | |
301 | "description": "invalid tree", | |
302 | "data": { | |
303 | "meta": "root", | |
304 | "nodes": [ | |
305 | { | |
306 | "value": 1, | |
307 | "subtree": { | |
308 | "meta": "child", | |
309 | "nodes": [ | |
310 | {"value": "string is invalid"}, | |
311 | {"value": 1.2} | |
312 | ] | |
313 | } | |
314 | }, | |
315 | { | |
316 | "value": 2, | |
317 | "subtree": { | |
318 | "meta": "child", | |
319 | "nodes": [ | |
320 | {"value": 2.1}, | |
321 | {"value": 2.2} | |
322 | ] | |
323 | } | |
324 | } | |
325 | ] | |
326 | }, | |
327 | "valid": false | |
328 | } | |
329 | ] | |
330 | } | |
331 | ] |
0 | [ | |
1 | { | |
2 | "description": "remote ref", | |
3 | "schema": {"$ref": "http://localhost:1234/integer.json"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "remote ref valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "remote ref invalid", | |
12 | "data": "a", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "fragment within remote ref", | |
19 | "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "remote fragment valid", | |
23 | "data": 1, | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "remote fragment invalid", | |
28 | "data": "a", | |
29 | "valid": false | |
30 | } | |
31 | ] | |
32 | }, | |
33 | { | |
34 | "description": "ref within remote ref", | |
35 | "schema": { | |
36 | "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" | |
37 | }, | |
38 | "tests": [ | |
39 | { | |
40 | "description": "ref within ref valid", | |
41 | "data": 1, | |
42 | "valid": true | |
43 | }, | |
44 | { | |
45 | "description": "ref within ref invalid", | |
46 | "data": "a", | |
47 | "valid": false | |
48 | } | |
49 | ] | |
50 | }, | |
51 | { | |
52 | "description": "base URI change", | |
53 | "schema": { | |
54 | "$id": "http://localhost:1234/", | |
55 | "items": { | |
56 | "$id": "folder/", | |
57 | "items": {"$ref": "folderInteger.json"} | |
58 | } | |
59 | }, | |
60 | "tests": [ | |
61 | { | |
62 | "description": "base URI change ref valid", | |
63 | "data": [[1]], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "base URI change ref invalid", | |
68 | "data": [["a"]], | |
69 | "valid": false | |
70 | } | |
71 | ] | |
72 | }, | |
73 | { | |
74 | "description": "base URI change - change folder", | |
75 | "schema": { | |
76 | "$id": "http://localhost:1234/scope_change_defs1.json", | |
77 | "type" : "object", | |
78 | "properties": { | |
79 | "list": {"$ref": "#/definitions/baz"} | |
80 | }, | |
81 | "definitions": { | |
82 | "baz": { | |
83 | "$id": "folder/", | |
84 | "type": "array", | |
85 | "items": {"$ref": "folderInteger.json"} | |
86 | } | |
87 | } | |
88 | }, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "number is valid", | |
92 | "data": {"list": [1]}, | |
93 | "valid": true | |
94 | }, | |
95 | { | |
96 | "description": "string is invalid", | |
97 | "data": {"list": ["a"]}, | |
98 | "valid": false | |
99 | } | |
100 | ] | |
101 | }, | |
102 | { | |
103 | "description": "base URI change - change folder in subschema", | |
104 | "schema": { | |
105 | "$id": "http://localhost:1234/scope_change_defs2.json", | |
106 | "type" : "object", | |
107 | "properties": { | |
108 | "list": {"$ref": "#/definitions/baz/definitions/bar"} | |
109 | }, | |
110 | "definitions": { | |
111 | "baz": { | |
112 | "$id": "folder/", | |
113 | "definitions": { | |
114 | "bar": { | |
115 | "type": "array", | |
116 | "items": {"$ref": "folderInteger.json"} | |
117 | } | |
118 | } | |
119 | } | |
120 | } | |
121 | }, | |
122 | "tests": [ | |
123 | { | |
124 | "description": "number is valid", | |
125 | "data": {"list": [1]}, | |
126 | "valid": true | |
127 | }, | |
128 | { | |
129 | "description": "string is invalid", | |
130 | "data": {"list": ["a"]}, | |
131 | "valid": false | |
132 | } | |
133 | ] | |
134 | }, | |
135 | { | |
136 | "description": "root ref in remote ref", | |
137 | "schema": { | |
138 | "$id": "http://localhost:1234/object", | |
139 | "type": "object", | |
140 | "properties": { | |
141 | "name": {"$ref": "name.json#/definitions/orNull"} | |
142 | } | |
143 | }, | |
144 | "tests": [ | |
145 | { | |
146 | "description": "string is valid", | |
147 | "data": { | |
148 | "name": "foo" | |
149 | }, | |
150 | "valid": true | |
151 | }, | |
152 | { | |
153 | "description": "null is valid", | |
154 | "data": { | |
155 | "name": null | |
156 | }, | |
157 | "valid": true | |
158 | }, | |
159 | { | |
160 | "description": "object is invalid", | |
161 | "data": { | |
162 | "name": { | |
163 | "name": null | |
164 | } | |
165 | }, | |
166 | "valid": false | |
167 | } | |
168 | ] | |
169 | } | |
170 | ] |
0 | [ | |
1 | { | |
2 | "description": "required validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {}, | |
6 | "bar": {} | |
7 | }, | |
8 | "required": ["foo"] | |
9 | }, | |
10 | "tests": [ | |
11 | { | |
12 | "description": "present required property is valid", | |
13 | "data": {"foo": 1}, | |
14 | "valid": true | |
15 | }, | |
16 | { | |
17 | "description": "non-present required property is invalid", | |
18 | "data": {"bar": 1}, | |
19 | "valid": false | |
20 | }, | |
21 | { | |
22 | "description": "ignores arrays", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "ignores strings", | |
28 | "data": "", | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "ignores other non-objects", | |
33 | "data": 12, | |
34 | "valid": true | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "required default validation", | |
40 | "schema": { | |
41 | "properties": { | |
42 | "foo": {} | |
43 | } | |
44 | }, | |
45 | "tests": [ | |
46 | { | |
47 | "description": "not required by default", | |
48 | "data": {}, | |
49 | "valid": true | |
50 | } | |
51 | ] | |
52 | }, | |
53 | { | |
54 | "description": "required with empty array", | |
55 | "schema": { | |
56 | "properties": { | |
57 | "foo": {} | |
58 | }, | |
59 | "required": [] | |
60 | }, | |
61 | "tests": [ | |
62 | { | |
63 | "description": "property not required", | |
64 | "data": {}, | |
65 | "valid": true | |
66 | } | |
67 | ] | |
68 | } | |
69 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer type matches integers", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "an integer is an integer", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a float is not an integer", | |
12 | "data": 1.1, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "a string is not an integer", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "a string is still not an integer, even if it looks like one", | |
22 | "data": "1", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "an object is not an integer", | |
27 | "data": {}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "an array is not an integer", | |
32 | "data": [], | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "a boolean is not an integer", | |
37 | "data": true, | |
38 | "valid": false | |
39 | }, | |
40 | { | |
41 | "description": "null is not an integer", | |
42 | "data": null, | |
43 | "valid": false | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "number type matches numbers", | |
49 | "schema": {"type": "number"}, | |
50 | "tests": [ | |
51 | { | |
52 | "description": "an integer is a number", | |
53 | "data": 1, | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "a float is a number", | |
58 | "data": 1.1, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a string is not a number", | |
63 | "data": "foo", | |
64 | "valid": false | |
65 | }, | |
66 | { | |
67 | "description": "a string is still not a number, even if it looks like one", | |
68 | "data": "1", | |
69 | "valid": false | |
70 | }, | |
71 | { | |
72 | "description": "an object is not a number", | |
73 | "data": {}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an array is not a number", | |
78 | "data": [], | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "a boolean is not a number", | |
83 | "data": true, | |
84 | "valid": false | |
85 | }, | |
86 | { | |
87 | "description": "null is not a number", | |
88 | "data": null, | |
89 | "valid": false | |
90 | } | |
91 | ] | |
92 | }, | |
93 | { | |
94 | "description": "string type matches strings", | |
95 | "schema": {"type": "string"}, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "1 is not a string", | |
99 | "data": 1, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "a float is not a string", | |
104 | "data": 1.1, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "a string is a string", | |
109 | "data": "foo", | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "a string is still a string, even if it looks like a number", | |
114 | "data": "1", | |
115 | "valid": true | |
116 | }, | |
117 | { | |
118 | "description": "an object is not a string", | |
119 | "data": {}, | |
120 | "valid": false | |
121 | }, | |
122 | { | |
123 | "description": "an array is not a string", | |
124 | "data": [], | |
125 | "valid": false | |
126 | }, | |
127 | { | |
128 | "description": "a boolean is not a string", | |
129 | "data": true, | |
130 | "valid": false | |
131 | }, | |
132 | { | |
133 | "description": "null is not a string", | |
134 | "data": null, | |
135 | "valid": false | |
136 | } | |
137 | ] | |
138 | }, | |
139 | { | |
140 | "description": "object type matches objects", | |
141 | "schema": {"type": "object"}, | |
142 | "tests": [ | |
143 | { | |
144 | "description": "an integer is not an object", | |
145 | "data": 1, | |
146 | "valid": false | |
147 | }, | |
148 | { | |
149 | "description": "a float is not an object", | |
150 | "data": 1.1, | |
151 | "valid": false | |
152 | }, | |
153 | { | |
154 | "description": "a string is not an object", | |
155 | "data": "foo", | |
156 | "valid": false | |
157 | }, | |
158 | { | |
159 | "description": "an object is an object", | |
160 | "data": {}, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "an array is not an object", | |
165 | "data": [], | |
166 | "valid": false | |
167 | }, | |
168 | { | |
169 | "description": "a boolean is not an object", | |
170 | "data": true, | |
171 | "valid": false | |
172 | }, | |
173 | { | |
174 | "description": "null is not an object", | |
175 | "data": null, | |
176 | "valid": false | |
177 | } | |
178 | ] | |
179 | }, | |
180 | { | |
181 | "description": "array type matches arrays", | |
182 | "schema": {"type": "array"}, | |
183 | "tests": [ | |
184 | { | |
185 | "description": "an integer is not an array", | |
186 | "data": 1, | |
187 | "valid": false | |
188 | }, | |
189 | { | |
190 | "description": "a float is not an array", | |
191 | "data": 1.1, | |
192 | "valid": false | |
193 | }, | |
194 | { | |
195 | "description": "a string is not an array", | |
196 | "data": "foo", | |
197 | "valid": false | |
198 | }, | |
199 | { | |
200 | "description": "an object is not an array", | |
201 | "data": {}, | |
202 | "valid": false | |
203 | }, | |
204 | { | |
205 | "description": "an array is an array", | |
206 | "data": [], | |
207 | "valid": true | |
208 | }, | |
209 | { | |
210 | "description": "a boolean is not an array", | |
211 | "data": true, | |
212 | "valid": false | |
213 | }, | |
214 | { | |
215 | "description": "null is not an array", | |
216 | "data": null, | |
217 | "valid": false | |
218 | } | |
219 | ] | |
220 | }, | |
221 | { | |
222 | "description": "boolean type matches booleans", | |
223 | "schema": {"type": "boolean"}, | |
224 | "tests": [ | |
225 | { | |
226 | "description": "an integer is not a boolean", | |
227 | "data": 1, | |
228 | "valid": false | |
229 | }, | |
230 | { | |
231 | "description": "a float is not a boolean", | |
232 | "data": 1.1, | |
233 | "valid": false | |
234 | }, | |
235 | { | |
236 | "description": "a string is not a boolean", | |
237 | "data": "foo", | |
238 | "valid": false | |
239 | }, | |
240 | { | |
241 | "description": "an object is not a boolean", | |
242 | "data": {}, | |
243 | "valid": false | |
244 | }, | |
245 | { | |
246 | "description": "an array is not a boolean", | |
247 | "data": [], | |
248 | "valid": false | |
249 | }, | |
250 | { | |
251 | "description": "a boolean is a boolean", | |
252 | "data": true, | |
253 | "valid": true | |
254 | }, | |
255 | { | |
256 | "description": "null is not a boolean", | |
257 | "data": null, | |
258 | "valid": false | |
259 | } | |
260 | ] | |
261 | }, | |
262 | { | |
263 | "description": "null type matches only the null object", | |
264 | "schema": {"type": "null"}, | |
265 | "tests": [ | |
266 | { | |
267 | "description": "an integer is not null", | |
268 | "data": 1, | |
269 | "valid": false | |
270 | }, | |
271 | { | |
272 | "description": "a float is not null", | |
273 | "data": 1.1, | |
274 | "valid": false | |
275 | }, | |
276 | { | |
277 | "description": "a string is not null", | |
278 | "data": "foo", | |
279 | "valid": false | |
280 | }, | |
281 | { | |
282 | "description": "an object is not null", | |
283 | "data": {}, | |
284 | "valid": false | |
285 | }, | |
286 | { | |
287 | "description": "an array is not null", | |
288 | "data": [], | |
289 | "valid": false | |
290 | }, | |
291 | { | |
292 | "description": "a boolean is not null", | |
293 | "data": true, | |
294 | "valid": false | |
295 | }, | |
296 | { | |
297 | "description": "null is null", | |
298 | "data": null, | |
299 | "valid": true | |
300 | } | |
301 | ] | |
302 | }, | |
303 | { | |
304 | "description": "multiple types can be specified in an array", | |
305 | "schema": {"type": ["integer", "string"]}, | |
306 | "tests": [ | |
307 | { | |
308 | "description": "an integer is valid", | |
309 | "data": 1, | |
310 | "valid": true | |
311 | }, | |
312 | { | |
313 | "description": "a string is valid", | |
314 | "data": "foo", | |
315 | "valid": true | |
316 | }, | |
317 | { | |
318 | "description": "a float is invalid", | |
319 | "data": 1.1, | |
320 | "valid": false | |
321 | }, | |
322 | { | |
323 | "description": "an object is invalid", | |
324 | "data": {}, | |
325 | "valid": false | |
326 | }, | |
327 | { | |
328 | "description": "an array is invalid", | |
329 | "data": [], | |
330 | "valid": false | |
331 | }, | |
332 | { | |
333 | "description": "a boolean is invalid", | |
334 | "data": true, | |
335 | "valid": false | |
336 | }, | |
337 | { | |
338 | "description": "null is invalid", | |
339 | "data": null, | |
340 | "valid": false | |
341 | } | |
342 | ] | |
343 | } | |
344 | ] |
0 | [ | |
1 | { | |
2 | "description": "uniqueItems validation", | |
3 | "schema": {"uniqueItems": true}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "unique array of integers is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "non-unique array of integers is invalid", | |
12 | "data": [1, 1], | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "numbers are unique if mathematically unequal", | |
17 | "data": [1.0, 1.00, 1], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "unique array of objects is valid", | |
22 | "data": [{"foo": "bar"}, {"foo": "baz"}], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "non-unique array of objects is invalid", | |
27 | "data": [{"foo": "bar"}, {"foo": "bar"}], | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "unique array of nested objects is valid", | |
32 | "data": [ | |
33 | {"foo": {"bar" : {"baz" : true}}}, | |
34 | {"foo": {"bar" : {"baz" : false}}} | |
35 | ], | |
36 | "valid": true | |
37 | }, | |
38 | { | |
39 | "description": "non-unique array of nested objects is invalid", | |
40 | "data": [ | |
41 | {"foo": {"bar" : {"baz" : true}}}, | |
42 | {"foo": {"bar" : {"baz" : true}}} | |
43 | ], | |
44 | "valid": false | |
45 | }, | |
46 | { | |
47 | "description": "unique array of arrays is valid", | |
48 | "data": [["foo"], ["bar"]], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "non-unique array of arrays is invalid", | |
53 | "data": [["foo"], ["foo"]], | |
54 | "valid": false | |
55 | }, | |
56 | { | |
57 | "description": "1 and true are unique", | |
58 | "data": [1, true], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "0 and false are unique", | |
63 | "data": [0, false], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "unique heterogeneous types are valid", | |
68 | "data": [{}, [1], true, null, 1], | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "non-unique heterogeneous types are invalid", | |
73 | "data": [{}, [1], true, null, {}, 1], | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | } | |
78 | ] |
0 | [ | |
1 | { | |
2 | "description": "additionalItems as schema", | |
3 | "schema": { | |
4 | "items": [{}], | |
5 | "additionalItems": {"type": "integer"} | |
6 | }, | |
7 | "tests": [ | |
8 | { | |
9 | "description": "additional items match schema", | |
10 | "data": [ null, 2, 3, 4 ], | |
11 | "valid": true | |
12 | }, | |
13 | { | |
14 | "description": "additional items do not match schema", | |
15 | "data": [ null, 2, 3, "foo" ], | |
16 | "valid": false | |
17 | } | |
18 | ] | |
19 | }, | |
20 | { | |
21 | "description": "items is schema, no additionalItems", | |
22 | "schema": { | |
23 | "items": {}, | |
24 | "additionalItems": false | |
25 | }, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "all items match schema", | |
29 | "data": [ 1, 2, 3, 4, 5 ], | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "array of items with no additionalItems", | |
36 | "schema": { | |
37 | "items": [{}, {}, {}], | |
38 | "additionalItems": false | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "fewer number of items present", | |
43 | "data": [ 1, 2 ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "equal number of items present", | |
48 | "data": [ 1, 2, 3 ], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "additional items are not permitted", | |
53 | "data": [ 1, 2, 3, 4 ], | |
54 | "valid": false | |
55 | } | |
56 | ] | |
57 | }, | |
58 | { | |
59 | "description": "additionalItems as false without items", | |
60 | "schema": {"additionalItems": false}, | |
61 | "tests": [ | |
62 | { | |
63 | "description": | |
64 | "items defaults to empty schema so everything is valid", | |
65 | "data": [ 1, 2, 3, 4, 5 ], | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "ignores non-arrays", | |
70 | "data": {"foo" : "bar"}, | |
71 | "valid": true | |
72 | } | |
73 | ] | |
74 | }, | |
75 | { | |
76 | "description": "additionalItems are allowed by default", | |
77 | "schema": {"items": [{"type": "integer"}]}, | |
78 | "tests": [ | |
79 | { | |
80 | "description": "only the first item is validated", | |
81 | "data": [1, "foo", false], | |
82 | "valid": true | |
83 | } | |
84 | ] | |
85 | } | |
86 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "additionalProperties being false does not allow other properties", | |
4 | "schema": { | |
5 | "properties": {"foo": {}, "bar": {}}, | |
6 | "patternProperties": { "^v": {} }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "no additional properties is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "an additional property is invalid", | |
17 | "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobarbaz", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "patternProperties are not additional properties", | |
37 | "data": {"foo":1, "vroom": 2}, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "additionalProperties allows a schema which should validate", | |
45 | "schema": { | |
46 | "properties": {"foo": {}, "bar": {}}, | |
47 | "additionalProperties": {"type": "boolean"} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "no additional properties is valid", | |
52 | "data": {"foo": 1}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "an additional valid property is valid", | |
57 | "data": {"foo" : 1, "bar" : 2, "quux" : true}, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "an additional invalid property is invalid", | |
62 | "data": {"foo" : 1, "bar" : 2, "quux" : 12}, | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": | |
69 | "additionalProperties can exist by itself", | |
70 | "schema": { | |
71 | "additionalProperties": {"type": "boolean"} | |
72 | }, | |
73 | "tests": [ | |
74 | { | |
75 | "description": "an additional valid property is valid", | |
76 | "data": {"foo" : true}, | |
77 | "valid": true | |
78 | }, | |
79 | { | |
80 | "description": "an additional invalid property is invalid", | |
81 | "data": {"foo" : 1}, | |
82 | "valid": false | |
83 | } | |
84 | ] | |
85 | }, | |
86 | { | |
87 | "description": "additionalProperties are allowed by default", | |
88 | "schema": {"properties": {"foo": {}, "bar": {}}}, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "additional properties are allowed", | |
92 | "data": {"foo": 1, "bar": 2, "quux": true}, | |
93 | "valid": true | |
94 | } | |
95 | ] | |
96 | } | |
97 | ] |
0 | [ | |
1 | { | |
2 | "description": "allOf", | |
3 | "schema": { | |
4 | "allOf": [ | |
5 | { | |
6 | "properties": { | |
7 | "bar": {"type": "integer"} | |
8 | }, | |
9 | "required": ["bar"] | |
10 | }, | |
11 | { | |
12 | "properties": { | |
13 | "foo": {"type": "string"} | |
14 | }, | |
15 | "required": ["foo"] | |
16 | } | |
17 | ] | |
18 | }, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "allOf", | |
22 | "data": {"foo": "baz", "bar": 2}, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "mismatch second", | |
27 | "data": {"foo": "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "mismatch first", | |
32 | "data": {"bar": 2}, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "wrong type", | |
37 | "data": {"foo": "baz", "bar": "quux"}, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "allOf with base schema", | |
44 | "schema": { | |
45 | "properties": {"bar": {"type": "integer"}}, | |
46 | "required": ["bar"], | |
47 | "allOf" : [ | |
48 | { | |
49 | "properties": { | |
50 | "foo": {"type": "string"} | |
51 | }, | |
52 | "required": ["foo"] | |
53 | }, | |
54 | { | |
55 | "properties": { | |
56 | "baz": {"type": "null"} | |
57 | }, | |
58 | "required": ["baz"] | |
59 | } | |
60 | ] | |
61 | }, | |
62 | "tests": [ | |
63 | { | |
64 | "description": "valid", | |
65 | "data": {"foo": "quux", "bar": 2, "baz": null}, | |
66 | "valid": true | |
67 | }, | |
68 | { | |
69 | "description": "mismatch base schema", | |
70 | "data": {"foo": "quux", "baz": null}, | |
71 | "valid": false | |
72 | }, | |
73 | { | |
74 | "description": "mismatch first allOf", | |
75 | "data": {"bar": 2, "baz": null}, | |
76 | "valid": false | |
77 | }, | |
78 | { | |
79 | "description": "mismatch second allOf", | |
80 | "data": {"foo": "quux", "bar": 2}, | |
81 | "valid": false | |
82 | }, | |
83 | { | |
84 | "description": "mismatch both", | |
85 | "data": {"bar": 2}, | |
86 | "valid": false | |
87 | } | |
88 | ] | |
89 | }, | |
90 | { | |
91 | "description": "allOf simple types", | |
92 | "schema": { | |
93 | "allOf": [ | |
94 | {"maximum": 30}, | |
95 | {"minimum": 20} | |
96 | ] | |
97 | }, | |
98 | "tests": [ | |
99 | { | |
100 | "description": "valid", | |
101 | "data": 25, | |
102 | "valid": true | |
103 | }, | |
104 | { | |
105 | "description": "mismatch one", | |
106 | "data": 35, | |
107 | "valid": false | |
108 | } | |
109 | ] | |
110 | }, | |
111 | { | |
112 | "description": "allOf with boolean schemas, all true", | |
113 | "schema": {"allOf": [true, true]}, | |
114 | "tests": [ | |
115 | { | |
116 | "description": "any value is valid", | |
117 | "data": "foo", | |
118 | "valid": true | |
119 | } | |
120 | ] | |
121 | }, | |
122 | { | |
123 | "description": "allOf with boolean schemas, some false", | |
124 | "schema": {"allOf": [true, false]}, | |
125 | "tests": [ | |
126 | { | |
127 | "description": "any value is invalid", | |
128 | "data": "foo", | |
129 | "valid": false | |
130 | } | |
131 | ] | |
132 | }, | |
133 | { | |
134 | "description": "allOf with boolean schemas, all false", | |
135 | "schema": {"allOf": [false, false]}, | |
136 | "tests": [ | |
137 | { | |
138 | "description": "any value is invalid", | |
139 | "data": "foo", | |
140 | "valid": false | |
141 | } | |
142 | ] | |
143 | } | |
144 | ] |
0 | [ | |
1 | { | |
2 | "description": "anyOf", | |
3 | "schema": { | |
4 | "anyOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first anyOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second anyOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both anyOf valid", | |
26 | "data": 3, | |
27 | "valid": true | |
28 | }, | |
29 | { | |
30 | "description": "neither anyOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "anyOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "anyOf" : [ | |
41 | { | |
42 | "maxLength": 2 | |
43 | }, | |
44 | { | |
45 | "minLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one anyOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both anyOf invalid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "anyOf with boolean schemas, all true", | |
69 | "schema": {"anyOf": [true, true]}, | |
70 | "tests": [ | |
71 | { | |
72 | "description": "any value is valid", | |
73 | "data": "foo", | |
74 | "valid": true | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "anyOf with boolean schemas, some true", | |
80 | "schema": {"anyOf": [true, false]}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any value is valid", | |
84 | "data": "foo", | |
85 | "valid": true | |
86 | } | |
87 | ] | |
88 | }, | |
89 | { | |
90 | "description": "anyOf with boolean schemas, all false", | |
91 | "schema": {"anyOf": [false, false]}, | |
92 | "tests": [ | |
93 | { | |
94 | "description": "any value is invalid", | |
95 | "data": "foo", | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "anyOf complex types", | |
102 | "schema": { | |
103 | "anyOf": [ | |
104 | { | |
105 | "properties": { | |
106 | "bar": {"type": "integer"} | |
107 | }, | |
108 | "required": ["bar"] | |
109 | }, | |
110 | { | |
111 | "properties": { | |
112 | "foo": {"type": "string"} | |
113 | }, | |
114 | "required": ["foo"] | |
115 | } | |
116 | ] | |
117 | }, | |
118 | "tests": [ | |
119 | { | |
120 | "description": "first anyOf valid (complex)", | |
121 | "data": {"bar": 2}, | |
122 | "valid": true | |
123 | }, | |
124 | { | |
125 | "description": "second anyOf valid (complex)", | |
126 | "data": {"foo": "baz"}, | |
127 | "valid": true | |
128 | }, | |
129 | { | |
130 | "description": "both anyOf valid (complex)", | |
131 | "data": {"foo": "baz", "bar": 2}, | |
132 | "valid": true | |
133 | }, | |
134 | { | |
135 | "description": "neither anyOf valid (complex)", | |
136 | "data": {"foo": 2, "bar": "quux"}, | |
137 | "valid": false | |
138 | } | |
139 | ] | |
140 | } | |
141 | ] |
0 | [ | |
1 | { | |
2 | "description": "boolean schema 'true'", | |
3 | "schema": true, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "number is valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "string is valid", | |
12 | "data": "foo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "boolean true is valid", | |
17 | "data": true, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "boolean false is valid", | |
22 | "data": false, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "null is valid", | |
27 | "data": null, | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "object is valid", | |
32 | "data": {"foo": "bar"}, | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "empty object is valid", | |
37 | "data": {}, | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "array is valid", | |
42 | "data": ["foo"], | |
43 | "valid": true | |
44 | }, | |
45 | { | |
46 | "description": "empty array is valid", | |
47 | "data": [], | |
48 | "valid": true | |
49 | } | |
50 | ] | |
51 | }, | |
52 | { | |
53 | "description": "boolean schema 'false'", | |
54 | "schema": false, | |
55 | "tests": [ | |
56 | { | |
57 | "description": "number is invalid", | |
58 | "data": 1, | |
59 | "valid": false | |
60 | }, | |
61 | { | |
62 | "description": "string is invalid", | |
63 | "data": "foo", | |
64 | "valid": false | |
65 | }, | |
66 | { | |
67 | "description": "boolean true is invalid", | |
68 | "data": true, | |
69 | "valid": false | |
70 | }, | |
71 | { | |
72 | "description": "boolean false is invalid", | |
73 | "data": false, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "null is invalid", | |
78 | "data": null, | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "object is invalid", | |
83 | "data": {"foo": "bar"}, | |
84 | "valid": false | |
85 | }, | |
86 | { | |
87 | "description": "empty object is invalid", | |
88 | "data": {}, | |
89 | "valid": false | |
90 | }, | |
91 | { | |
92 | "description": "array is invalid", | |
93 | "data": ["foo"], | |
94 | "valid": false | |
95 | }, | |
96 | { | |
97 | "description": "empty array is invalid", | |
98 | "data": [], | |
99 | "valid": false | |
100 | } | |
101 | ] | |
102 | } | |
103 | ] |
0 | [ | |
1 | { | |
2 | "description": "const validation", | |
3 | "schema": {"const": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "same value is valid", | |
7 | "data": 2, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "another value is invalid", | |
12 | "data": 5, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "another type is invalid", | |
17 | "data": "a", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "const with object", | |
24 | "schema": {"const": {"foo": "bar", "baz": "bax"}}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "same object is valid", | |
28 | "data": {"foo": "bar", "baz": "bax"}, | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "same object with different property order is valid", | |
33 | "data": {"baz": "bax", "foo": "bar"}, | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "another object is invalid", | |
38 | "data": {"foo": "bar"}, | |
39 | "valid": false | |
40 | }, | |
41 | { | |
42 | "description": "another type is invalid", | |
43 | "data": [1, 2], | |
44 | "valid": false | |
45 | } | |
46 | ] | |
47 | }, | |
48 | { | |
49 | "description": "const with array", | |
50 | "schema": {"const": [{ "foo": "bar" }]}, | |
51 | "tests": [ | |
52 | { | |
53 | "description": "same array is valid", | |
54 | "data": [{"foo": "bar"}], | |
55 | "valid": true | |
56 | }, | |
57 | { | |
58 | "description": "another array item is invalid", | |
59 | "data": [2], | |
60 | "valid": false | |
61 | }, | |
62 | { | |
63 | "description": "array with additional items is invalid", | |
64 | "data": [1, 2, 3], | |
65 | "valid": false | |
66 | } | |
67 | ] | |
68 | }, | |
69 | { | |
70 | "description": "const with null", | |
71 | "schema": {"const": null}, | |
72 | "tests": [ | |
73 | { | |
74 | "description": "null is valid", | |
75 | "data": null, | |
76 | "valid": true | |
77 | }, | |
78 | { | |
79 | "description": "not null is invalid", | |
80 | "data": 0, | |
81 | "valid": false | |
82 | } | |
83 | ] | |
84 | } | |
85 | ] |
0 | [ | |
1 | { | |
2 | "description": "contains keyword validation", | |
3 | "schema": { | |
4 | "contains": {"minimum": 5} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "array with item matching schema (5) is valid", | |
9 | "data": [3, 4, 5], | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "array with item matching schema (6) is valid", | |
14 | "data": [3, 4, 6], | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "array with two items matching schema (5, 6) is valid", | |
19 | "data": [3, 4, 5, 6], | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "array without items matching schema is invalid", | |
24 | "data": [2, 3, 4], | |
25 | "valid": false | |
26 | }, | |
27 | { | |
28 | "description": "empty array is invalid", | |
29 | "data": [], | |
30 | "valid": false | |
31 | }, | |
32 | { | |
33 | "description": "not array is valid", | |
34 | "data": {}, | |
35 | "valid": true | |
36 | } | |
37 | ] | |
38 | }, | |
39 | { | |
40 | "description": "contains keyword with const keyword", | |
41 | "schema": { | |
42 | "contains": { "const": 5 } | |
43 | }, | |
44 | "tests": [ | |
45 | { | |
46 | "description": "array with item 5 is valid", | |
47 | "data": [3, 4, 5], | |
48 | "valid": true | |
49 | }, | |
50 | { | |
51 | "description": "array with two items 5 is valid", | |
52 | "data": [3, 4, 5, 5], | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "array without item 5 is invalid", | |
57 | "data": [1, 2, 3, 4], | |
58 | "valid": false | |
59 | } | |
60 | ] | |
61 | }, | |
62 | { | |
63 | "description": "contains keyword with boolean schema true", | |
64 | "schema": {"contains": true}, | |
65 | "tests": [ | |
66 | { | |
67 | "description": "any non-empty array is valid", | |
68 | "data": ["foo"], | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "empty array is invalid", | |
73 | "data": [], | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "contains keyword with boolean schema false", | |
80 | "schema": {"contains": false}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any non-empty array is invalid", | |
84 | "data": ["foo"], | |
85 | "valid": false | |
86 | }, | |
87 | { | |
88 | "description": "empty array is invalid", | |
89 | "data": [], | |
90 | "valid": false | |
91 | } | |
92 | ] | |
93 | } | |
94 | ] |
0 | [ | |
1 | { | |
2 | "description": "invalid type for default", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": { | |
6 | "type": "integer", | |
7 | "default": [] | |
8 | } | |
9 | } | |
10 | }, | |
11 | "tests": [ | |
12 | { | |
13 | "description": "valid when property is specified", | |
14 | "data": {"foo": 13}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "still valid when the invalid default is used", | |
19 | "data": {}, | |
20 | "valid": true | |
21 | } | |
22 | ] | |
23 | }, | |
24 | { | |
25 | "description": "invalid string value for default", | |
26 | "schema": { | |
27 | "properties": { | |
28 | "bar": { | |
29 | "type": "string", | |
30 | "minLength": 4, | |
31 | "default": "bad" | |
32 | } | |
33 | } | |
34 | }, | |
35 | "tests": [ | |
36 | { | |
37 | "description": "valid when property is specified", | |
38 | "data": {"bar": "good"}, | |
39 | "valid": true | |
40 | }, | |
41 | { | |
42 | "description": "still valid when the invalid default is used", | |
43 | "data": {}, | |
44 | "valid": true | |
45 | } | |
46 | ] | |
47 | } | |
48 | ] |
0 | [ | |
1 | { | |
2 | "description": "valid definition", | |
3 | "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "valid definition schema", | |
7 | "data": { | |
8 | "definitions": { | |
9 | "foo": {"type": "integer"} | |
10 | } | |
11 | }, | |
12 | "valid": true | |
13 | } | |
14 | ] | |
15 | }, | |
16 | { | |
17 | "description": "invalid definition", | |
18 | "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, | |
19 | "tests": [ | |
20 | { | |
21 | "description": "invalid definition schema", | |
22 | "data": { | |
23 | "definitions": { | |
24 | "foo": {"type": 1} | |
25 | } | |
26 | }, | |
27 | "valid": false | |
28 | } | |
29 | ] | |
30 | } | |
31 | ] |
0 | [ | |
1 | { | |
2 | "description": "dependencies", | |
3 | "schema": { | |
4 | "dependencies": {"bar": ["foo"]} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "neither", | |
9 | "data": {}, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "nondependant", | |
14 | "data": {"foo": 1}, | |
15 | "valid": true | |
16 | }, | |
17 | { | |
18 | "description": "with dependency", | |
19 | "data": {"foo": 1, "bar": 2}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "missing dependency", | |
24 | "data": {"bar": 2}, | |
25 | "valid": false | |
26 | }, | |
27 | { | |
28 | "description": "ignores arrays", | |
29 | "data": ["bar"], | |
30 | "valid": true | |
31 | }, | |
32 | { | |
33 | "description": "ignores strings", | |
34 | "data": "foobar", | |
35 | "valid": true | |
36 | }, | |
37 | { | |
38 | "description": "ignores other non-objects", | |
39 | "data": 12, | |
40 | "valid": true | |
41 | } | |
42 | ] | |
43 | }, | |
44 | { | |
45 | "description": "dependencies with empty array", | |
46 | "schema": { | |
47 | "dependencies": {"bar": []} | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "empty object", | |
52 | "data": {}, | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "object with one property", | |
57 | "data": {"bar": 2}, | |
58 | "valid": true | |
59 | } | |
60 | ] | |
61 | }, | |
62 | { | |
63 | "description": "multiple dependencies", | |
64 | "schema": { | |
65 | "dependencies": {"quux": ["foo", "bar"]} | |
66 | }, | |
67 | "tests": [ | |
68 | { | |
69 | "description": "neither", | |
70 | "data": {}, | |
71 | "valid": true | |
72 | }, | |
73 | { | |
74 | "description": "nondependants", | |
75 | "data": {"foo": 1, "bar": 2}, | |
76 | "valid": true | |
77 | }, | |
78 | { | |
79 | "description": "with dependencies", | |
80 | "data": {"foo": 1, "bar": 2, "quux": 3}, | |
81 | "valid": true | |
82 | }, | |
83 | { | |
84 | "description": "missing dependency", | |
85 | "data": {"foo": 1, "quux": 2}, | |
86 | "valid": false | |
87 | }, | |
88 | { | |
89 | "description": "missing other dependency", | |
90 | "data": {"bar": 1, "quux": 2}, | |
91 | "valid": false | |
92 | }, | |
93 | { | |
94 | "description": "missing both dependencies", | |
95 | "data": {"quux": 1}, | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "multiple dependencies subschema", | |
102 | "schema": { | |
103 | "dependencies": { | |
104 | "bar": { | |
105 | "properties": { | |
106 | "foo": {"type": "integer"}, | |
107 | "bar": {"type": "integer"} | |
108 | } | |
109 | } | |
110 | } | |
111 | }, | |
112 | "tests": [ | |
113 | { | |
114 | "description": "valid", | |
115 | "data": {"foo": 1, "bar": 2}, | |
116 | "valid": true | |
117 | }, | |
118 | { | |
119 | "description": "no dependency", | |
120 | "data": {"foo": "quux"}, | |
121 | "valid": true | |
122 | }, | |
123 | { | |
124 | "description": "wrong type", | |
125 | "data": {"foo": "quux", "bar": 2}, | |
126 | "valid": false | |
127 | }, | |
128 | { | |
129 | "description": "wrong type other", | |
130 | "data": {"foo": 2, "bar": "quux"}, | |
131 | "valid": false | |
132 | }, | |
133 | { | |
134 | "description": "wrong type both", | |
135 | "data": {"foo": "quux", "bar": "quux"}, | |
136 | "valid": false | |
137 | } | |
138 | ] | |
139 | }, | |
140 | { | |
141 | "description": "dependencies with boolean subschemas", | |
142 | "schema": { | |
143 | "dependencies": { | |
144 | "foo": true, | |
145 | "bar": false | |
146 | } | |
147 | }, | |
148 | "tests": [ | |
149 | { | |
150 | "description": "object with property having schema true is valid", | |
151 | "data": {"foo": 1}, | |
152 | "valid": true | |
153 | }, | |
154 | { | |
155 | "description": "object with property having schema false is invalid", | |
156 | "data": {"bar": 2}, | |
157 | "valid": false | |
158 | }, | |
159 | { | |
160 | "description": "object with both properties is invalid", | |
161 | "data": {"foo": 1, "bar": 2}, | |
162 | "valid": false | |
163 | }, | |
164 | { | |
165 | "description": "empty object is valid", | |
166 | "data": {}, | |
167 | "valid": true | |
168 | } | |
169 | ] | |
170 | } | |
171 | ] |
0 | [ | |
1 | { | |
2 | "description": "simple enum validation", | |
3 | "schema": {"enum": [1, 2, 3]}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "one of the enum is valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "something else is invalid", | |
12 | "data": 4, | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "heterogeneous enum validation", | |
19 | "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "one of the enum is valid", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "something else is invalid", | |
28 | "data": null, | |
29 | "valid": false | |
30 | }, | |
31 | { | |
32 | "description": "objects are deep compared", | |
33 | "data": {"foo": false}, | |
34 | "valid": false | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "enums in properties", | |
40 | "schema": { | |
41 | "type":"object", | |
42 | "properties": { | |
43 | "foo": {"enum":["foo"]}, | |
44 | "bar": {"enum":["bar"]} | |
45 | }, | |
46 | "required": ["bar"] | |
47 | }, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "both properties are valid", | |
51 | "data": {"foo":"foo", "bar":"bar"}, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "missing optional property is valid", | |
56 | "data": {"bar":"bar"}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "missing required property is invalid", | |
61 | "data": {"foo":"foo"}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "missing all properties is invalid", | |
66 | "data": {}, | |
67 | "valid": false | |
68 | } | |
69 | ] | |
70 | } | |
71 | ] |
0 | [ | |
1 | { | |
2 | "description": "exclusiveMaximum validation", | |
3 | "schema": { | |
4 | "exclusiveMaximum": 3.0 | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "below the exclusiveMaximum is valid", | |
9 | "data": 2.2, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "boundary point is invalid", | |
14 | "data": 3.0, | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "above the exclusiveMaximum is invalid", | |
19 | "data": 3.5, | |
20 | "valid": false | |
21 | }, | |
22 | { | |
23 | "description": "ignores non-numbers", | |
24 | "data": "x", | |
25 | "valid": true | |
26 | } | |
27 | ] | |
28 | } | |
29 | ] |
0 | [ | |
1 | { | |
2 | "description": "exclusiveMinimum validation", | |
3 | "schema": { | |
4 | "exclusiveMinimum": 1.1 | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "above the exclusiveMinimum is valid", | |
9 | "data": 1.2, | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "boundary point is invalid", | |
14 | "data": 1.1, | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "below the exclusiveMinimum is invalid", | |
19 | "data": 0.6, | |
20 | "valid": false | |
21 | }, | |
22 | { | |
23 | "description": "ignores non-numbers", | |
24 | "data": "x", | |
25 | "valid": true | |
26 | } | |
27 | ] | |
28 | } | |
29 | ] |
0 | [ | |
1 | { | |
2 | "description": "ignore if without then or else", | |
3 | "schema": { | |
4 | "if": { | |
5 | "const": 0 | |
6 | } | |
7 | }, | |
8 | "tests": [ | |
9 | { | |
10 | "description": "valid when valid against lone if", | |
11 | "data": 0, | |
12 | "valid": true | |
13 | }, | |
14 | { | |
15 | "description": "valid when invailid against lone if", | |
16 | "data": "hello", | |
17 | "valid": true | |
18 | } | |
19 | ] | |
20 | }, | |
21 | { | |
22 | "description": "ignore then without if", | |
23 | "schema": { | |
24 | "then": { | |
25 | "const": 0 | |
26 | } | |
27 | }, | |
28 | "tests": [ | |
29 | { | |
30 | "description": "valid when valid against lone then", | |
31 | "data": 0, | |
32 | "valid": true | |
33 | }, | |
34 | { | |
35 | "description": "valid when invailid against lone then", | |
36 | "data": "hello", | |
37 | "valid": true | |
38 | } | |
39 | ] | |
40 | }, | |
41 | { | |
42 | "description": "ignore else without if", | |
43 | "schema": { | |
44 | "else": { | |
45 | "const": 0 | |
46 | } | |
47 | }, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "valid when valid against lone else", | |
51 | "data": 0, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "valid when invailid against lone else", | |
56 | "data": "hello", | |
57 | "valid": true | |
58 | } | |
59 | ] | |
60 | }, | |
61 | { | |
62 | "description": "if and then without else", | |
63 | "schema": { | |
64 | "if": { | |
65 | "exclusiveMaximum": 0 | |
66 | }, | |
67 | "then": { | |
68 | "minimum": -10 | |
69 | } | |
70 | }, | |
71 | "tests": [ | |
72 | { | |
73 | "description": "valid through then", | |
74 | "data": -1, | |
75 | "valid": true | |
76 | }, | |
77 | { | |
78 | "description": "invalid through then", | |
79 | "data": -100, | |
80 | "valid": false | |
81 | }, | |
82 | { | |
83 | "description": "valid when if test fails", | |
84 | "data": 3, | |
85 | "valid": true | |
86 | } | |
87 | ] | |
88 | }, | |
89 | { | |
90 | "description": "if and else without then", | |
91 | "schema": { | |
92 | "if": { | |
93 | "exclusiveMaximum": 0 | |
94 | }, | |
95 | "else": { | |
96 | "multipleOf": 2 | |
97 | } | |
98 | }, | |
99 | "tests": [ | |
100 | { | |
101 | "description": "valid when if test passes", | |
102 | "data": -1, | |
103 | "valid": true | |
104 | }, | |
105 | { | |
106 | "description": "valid through else", | |
107 | "data": 4, | |
108 | "valid": true | |
109 | }, | |
110 | { | |
111 | "description": "invalid through else", | |
112 | "data": 3, | |
113 | "valid": false | |
114 | } | |
115 | ] | |
116 | }, | |
117 | { | |
118 | "description": "validate against correct branch, then vs else", | |
119 | "schema": { | |
120 | "if": { | |
121 | "exclusiveMaximum": 0 | |
122 | }, | |
123 | "then": { | |
124 | "minimum": -10 | |
125 | }, | |
126 | "else": { | |
127 | "multipleOf": 2 | |
128 | } | |
129 | }, | |
130 | "tests": [ | |
131 | { | |
132 | "description": "valid through then", | |
133 | "data": -1, | |
134 | "valid": true | |
135 | }, | |
136 | { | |
137 | "description": "invalid through then", | |
138 | "data": -100, | |
139 | "valid": false | |
140 | }, | |
141 | { | |
142 | "description": "valid through else", | |
143 | "data": 4, | |
144 | "valid": true | |
145 | }, | |
146 | { | |
147 | "description": "invalid through else", | |
148 | "data": 3, | |
149 | "valid": false | |
150 | } | |
151 | ] | |
152 | }, | |
153 | { | |
154 | "description": "non-interference across combined schemas", | |
155 | "schema": { | |
156 | "allOf": [ | |
157 | { | |
158 | "if": { | |
159 | "exclusiveMaximum": 0 | |
160 | } | |
161 | }, | |
162 | { | |
163 | "then": { | |
164 | "minimum": -10 | |
165 | } | |
166 | }, | |
167 | { | |
168 | "else": { | |
169 | "multipleOf": 2 | |
170 | } | |
171 | } | |
172 | ] | |
173 | }, | |
174 | "tests": [ | |
175 | { | |
176 | "description": "valid, but woud have been invalid through then", | |
177 | "data": -100, | |
178 | "valid": true | |
179 | }, | |
180 | { | |
181 | "description": "valid, but would have been invalid through else", | |
182 | "data": 3, | |
183 | "valid": true | |
184 | } | |
185 | ] | |
186 | } | |
187 | ] |
0 | [ | |
1 | { | |
2 | "description": "a schema given for items", | |
3 | "schema": { | |
4 | "items": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "valid items", | |
9 | "data": [ 1, 2, 3 ], | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "wrong type of items", | |
14 | "data": [1, "x"], | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "ignores non-arrays", | |
19 | "data": {"foo" : "bar"}, | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "JavaScript pseudo-array is valid", | |
24 | "data": { | |
25 | "0": "invalid", | |
26 | "length": 1 | |
27 | }, | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "an array of schemas for items", | |
34 | "schema": { | |
35 | "items": [ | |
36 | {"type": "integer"}, | |
37 | {"type": "string"} | |
38 | ] | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "correct types", | |
43 | "data": [ 1, "foo" ], | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "wrong types", | |
48 | "data": [ "foo", 1 ], | |
49 | "valid": false | |
50 | }, | |
51 | { | |
52 | "description": "incomplete array of items", | |
53 | "data": [ 1 ], | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "array with additional items", | |
58 | "data": [ 1, "foo", true ], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "empty array", | |
63 | "data": [ ], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "JavaScript pseudo-array is valid", | |
68 | "data": { | |
69 | "0": "invalid", | |
70 | "1": "valid", | |
71 | "length": 2 | |
72 | }, | |
73 | "valid": true | |
74 | } | |
75 | ] | |
76 | }, | |
77 | { | |
78 | "description": "items with boolean schema (true)", | |
79 | "schema": {"items": true}, | |
80 | "tests": [ | |
81 | { | |
82 | "description": "any array is valid", | |
83 | "data": [ 1, "foo", true ], | |
84 | "valid": true | |
85 | }, | |
86 | { | |
87 | "description": "empty array is valid", | |
88 | "data": [], | |
89 | "valid": true | |
90 | } | |
91 | ] | |
92 | }, | |
93 | { | |
94 | "description": "items with boolean schema (false)", | |
95 | "schema": {"items": false}, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "any non-empty array is invalid", | |
99 | "data": [ 1, "foo", true ], | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "empty array is valid", | |
104 | "data": [], | |
105 | "valid": true | |
106 | } | |
107 | ] | |
108 | }, | |
109 | { | |
110 | "description": "items with boolean schemas", | |
111 | "schema": { | |
112 | "items": [true, false] | |
113 | }, | |
114 | "tests": [ | |
115 | { | |
116 | "description": "array with one item is valid", | |
117 | "data": [ 1 ], | |
118 | "valid": true | |
119 | }, | |
120 | { | |
121 | "description": "array with two items is invalid", | |
122 | "data": [ 1, "foo" ], | |
123 | "valid": false | |
124 | }, | |
125 | { | |
126 | "description": "empty array is valid", | |
127 | "data": [], | |
128 | "valid": true | |
129 | } | |
130 | ] | |
131 | } | |
132 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxItems validation", | |
3 | "schema": {"maxItems": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": [1], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1, 2], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": [1, 2, 3], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "foobar", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxLength validation", | |
3 | "schema": {"maxLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": "f", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 100, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "two supplementary Unicode code points is long enough", | |
27 | "data": "\uD83D\uDCA9\uD83D\uDCA9", | |
28 | "valid": true | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "maxProperties validation", | |
3 | "schema": {"maxProperties": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "shorter is valid", | |
7 | "data": {"foo": 1}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1, "bar": 2}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too long is invalid", | |
17 | "data": {"foo": 1, "bar": 2, "baz": 3}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [1, 2, 3], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "foobar", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "maximum validation", | |
3 | "schema": {"maximum": 3.0}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "below the maximum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 3.0, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "above the maximum is invalid", | |
17 | "data": 3.5, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "minItems validation", | |
3 | "schema": {"minItems": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": [1], | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": [], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-arrays", | |
22 | "data": "", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "minLength validation", | |
3 | "schema": {"minLength": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": "foo", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": "fo", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": "f", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-strings", | |
22 | "data": 1, | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "one supplementary Unicode code point is not long enough", | |
27 | "data": "\uD83D\uDCA9", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "minProperties validation", | |
3 | "schema": {"minProperties": 1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "longer is valid", | |
7 | "data": {"foo": 1, "bar": 2}, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "exact length is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "too short is invalid", | |
17 | "data": {}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores arrays", | |
22 | "data": [], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "ignores strings", | |
27 | "data": "", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores other non-objects", | |
32 | "data": 12, | |
33 | "valid": true | |
34 | } | |
35 | ] | |
36 | } | |
37 | ] |
0 | [ | |
1 | { | |
2 | "description": "minimum validation", | |
3 | "schema": {"minimum": 1.1}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "above the minimum is valid", | |
7 | "data": 2.6, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "boundary point is valid", | |
12 | "data": 1.1, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "below the minimum is invalid", | |
17 | "data": 0.6, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "ignores non-numbers", | |
22 | "data": "x", | |
23 | "valid": true | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "by int", | |
3 | "schema": {"multipleOf": 2}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "int by int", | |
7 | "data": 10, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "int by int fail", | |
12 | "data": 7, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-numbers", | |
17 | "data": "foo", | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "by number", | |
24 | "schema": {"multipleOf": 1.5}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "zero is multiple of anything", | |
28 | "data": 0, | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "4.5 is multiple of 1.5", | |
33 | "data": 4.5, | |
34 | "valid": true | |
35 | }, | |
36 | { | |
37 | "description": "35 is not multiple of 1.5", | |
38 | "data": 35, | |
39 | "valid": false | |
40 | } | |
41 | ] | |
42 | }, | |
43 | { | |
44 | "description": "by small number", | |
45 | "schema": {"multipleOf": 0.0001}, | |
46 | "tests": [ | |
47 | { | |
48 | "description": "0.0075 is multiple of 0.0001", | |
49 | "data": 0.0075, | |
50 | "valid": true | |
51 | }, | |
52 | { | |
53 | "description": "0.00751 is not multiple of 0.0001", | |
54 | "data": 0.00751, | |
55 | "valid": false | |
56 | } | |
57 | ] | |
58 | } | |
59 | ] |
0 | [ | |
1 | { | |
2 | "description": "not", | |
3 | "schema": { | |
4 | "not": {"type": "integer"} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "allowed", | |
9 | "data": "foo", | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "disallowed", | |
14 | "data": 1, | |
15 | "valid": false | |
16 | } | |
17 | ] | |
18 | }, | |
19 | { | |
20 | "description": "not multiple types", | |
21 | "schema": { | |
22 | "not": {"type": ["integer", "boolean"]} | |
23 | }, | |
24 | "tests": [ | |
25 | { | |
26 | "description": "valid", | |
27 | "data": "foo", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "mismatch", | |
32 | "data": 1, | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "other mismatch", | |
37 | "data": true, | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": "not more complex schema", | |
44 | "schema": { | |
45 | "not": { | |
46 | "type": "object", | |
47 | "properties": { | |
48 | "foo": { | |
49 | "type": "string" | |
50 | } | |
51 | } | |
52 | } | |
53 | }, | |
54 | "tests": [ | |
55 | { | |
56 | "description": "match", | |
57 | "data": 1, | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "other match", | |
62 | "data": {"foo": 1}, | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "mismatch", | |
67 | "data": {"foo": "bar"}, | |
68 | "valid": false | |
69 | } | |
70 | ] | |
71 | }, | |
72 | { | |
73 | "description": "forbidden property", | |
74 | "schema": { | |
75 | "properties": { | |
76 | "foo": { | |
77 | "not": {} | |
78 | } | |
79 | } | |
80 | }, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "property present", | |
84 | "data": {"foo": 1, "bar": 2}, | |
85 | "valid": false | |
86 | }, | |
87 | { | |
88 | "description": "property absent", | |
89 | "data": {"bar": 1, "baz": 2}, | |
90 | "valid": true | |
91 | } | |
92 | ] | |
93 | }, | |
94 | { | |
95 | "description": "not with boolean schema true", | |
96 | "schema": {"not": true}, | |
97 | "tests": [ | |
98 | { | |
99 | "description": "any value is invalid", | |
100 | "data": "foo", | |
101 | "valid": false | |
102 | } | |
103 | ] | |
104 | }, | |
105 | { | |
106 | "description": "not with boolean schema false", | |
107 | "schema": {"not": false}, | |
108 | "tests": [ | |
109 | { | |
110 | "description": "any value is valid", | |
111 | "data": "foo", | |
112 | "valid": true | |
113 | } | |
114 | ] | |
115 | } | |
116 | ] |
0 | [ | |
1 | { | |
2 | "description": "oneOf", | |
3 | "schema": { | |
4 | "oneOf": [ | |
5 | { | |
6 | "type": "integer" | |
7 | }, | |
8 | { | |
9 | "minimum": 2 | |
10 | } | |
11 | ] | |
12 | }, | |
13 | "tests": [ | |
14 | { | |
15 | "description": "first oneOf valid", | |
16 | "data": 1, | |
17 | "valid": true | |
18 | }, | |
19 | { | |
20 | "description": "second oneOf valid", | |
21 | "data": 2.5, | |
22 | "valid": true | |
23 | }, | |
24 | { | |
25 | "description": "both oneOf valid", | |
26 | "data": 3, | |
27 | "valid": false | |
28 | }, | |
29 | { | |
30 | "description": "neither oneOf valid", | |
31 | "data": 1.5, | |
32 | "valid": false | |
33 | } | |
34 | ] | |
35 | }, | |
36 | { | |
37 | "description": "oneOf with base schema", | |
38 | "schema": { | |
39 | "type": "string", | |
40 | "oneOf" : [ | |
41 | { | |
42 | "minLength": 2 | |
43 | }, | |
44 | { | |
45 | "maxLength": 4 | |
46 | } | |
47 | ] | |
48 | }, | |
49 | "tests": [ | |
50 | { | |
51 | "description": "mismatch base schema", | |
52 | "data": 3, | |
53 | "valid": false | |
54 | }, | |
55 | { | |
56 | "description": "one oneOf valid", | |
57 | "data": "foobar", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "both oneOf valid", | |
62 | "data": "foo", | |
63 | "valid": false | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "oneOf with boolean schemas, all true", | |
69 | "schema": {"oneOf": [true, true, true]}, | |
70 | "tests": [ | |
71 | { | |
72 | "description": "any value is invalid", | |
73 | "data": "foo", | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | }, | |
78 | { | |
79 | "description": "oneOf with boolean schemas, one true", | |
80 | "schema": {"oneOf": [true, false, false]}, | |
81 | "tests": [ | |
82 | { | |
83 | "description": "any value is valid", | |
84 | "data": "foo", | |
85 | "valid": true | |
86 | } | |
87 | ] | |
88 | }, | |
89 | { | |
90 | "description": "oneOf with boolean schemas, more than one true", | |
91 | "schema": {"oneOf": [true, true, false]}, | |
92 | "tests": [ | |
93 | { | |
94 | "description": "any value is invalid", | |
95 | "data": "foo", | |
96 | "valid": false | |
97 | } | |
98 | ] | |
99 | }, | |
100 | { | |
101 | "description": "oneOf with boolean schemas, all false", | |
102 | "schema": {"oneOf": [false, false, false]}, | |
103 | "tests": [ | |
104 | { | |
105 | "description": "any value is invalid", | |
106 | "data": "foo", | |
107 | "valid": false | |
108 | } | |
109 | ] | |
110 | }, | |
111 | { | |
112 | "description": "oneOf complex types", | |
113 | "schema": { | |
114 | "oneOf": [ | |
115 | { | |
116 | "properties": { | |
117 | "bar": {"type": "integer"} | |
118 | }, | |
119 | "required": ["bar"] | |
120 | }, | |
121 | { | |
122 | "properties": { | |
123 | "foo": {"type": "string"} | |
124 | }, | |
125 | "required": ["foo"] | |
126 | } | |
127 | ] | |
128 | }, | |
129 | "tests": [ | |
130 | { | |
131 | "description": "first oneOf valid (complex)", | |
132 | "data": {"bar": 2}, | |
133 | "valid": true | |
134 | }, | |
135 | { | |
136 | "description": "second oneOf valid (complex)", | |
137 | "data": {"foo": "baz"}, | |
138 | "valid": true | |
139 | }, | |
140 | { | |
141 | "description": "both oneOf valid (complex)", | |
142 | "data": {"foo": "baz", "bar": 2}, | |
143 | "valid": false | |
144 | }, | |
145 | { | |
146 | "description": "neither oneOf valid (complex)", | |
147 | "data": {"foo": 2, "bar": "quux"}, | |
148 | "valid": false | |
149 | } | |
150 | ] | |
151 | } | |
152 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a bignum is an integer", | |
7 | "data": 12345678910111213141516171819202122232425262728293031, | |
8 | "valid": true | |
9 | } | |
10 | ] | |
11 | }, | |
12 | { | |
13 | "description": "number", | |
14 | "schema": {"type": "number"}, | |
15 | "tests": [ | |
16 | { | |
17 | "description": "a bignum is a number", | |
18 | "data": 98249283749234923498293171823948729348710298301928331, | |
19 | "valid": true | |
20 | } | |
21 | ] | |
22 | }, | |
23 | { | |
24 | "description": "integer", | |
25 | "schema": {"type": "integer"}, | |
26 | "tests": [ | |
27 | { | |
28 | "description": "a negative bignum is an integer", | |
29 | "data": -12345678910111213141516171819202122232425262728293031, | |
30 | "valid": true | |
31 | } | |
32 | ] | |
33 | }, | |
34 | { | |
35 | "description": "number", | |
36 | "schema": {"type": "number"}, | |
37 | "tests": [ | |
38 | { | |
39 | "description": "a negative bignum is a number", | |
40 | "data": -98249283749234923498293171823948729348710298301928331, | |
41 | "valid": true | |
42 | } | |
43 | ] | |
44 | }, | |
45 | { | |
46 | "description": "string", | |
47 | "schema": {"type": "string"}, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "a bignum is not a string", | |
51 | "data": 98249283749234923498293171823948729348710298301928331, | |
52 | "valid": false | |
53 | } | |
54 | ] | |
55 | }, | |
56 | { | |
57 | "description": "integer comparison", | |
58 | "schema": {"maximum": 18446744073709551615}, | |
59 | "tests": [ | |
60 | { | |
61 | "description": "comparison works for high numbers", | |
62 | "data": 18446744073709551600, | |
63 | "valid": true | |
64 | } | |
65 | ] | |
66 | }, | |
67 | { | |
68 | "description": "float comparison with high precision", | |
69 | "schema": { | |
70 | "exclusiveMaximum": 972783798187987123879878123.18878137 | |
71 | }, | |
72 | "tests": [ | |
73 | { | |
74 | "description": "comparison works for high numbers", | |
75 | "data": 972783798187987123879878123.188781371, | |
76 | "valid": false | |
77 | } | |
78 | ] | |
79 | }, | |
80 | { | |
81 | "description": "integer comparison", | |
82 | "schema": {"minimum": -18446744073709551615}, | |
83 | "tests": [ | |
84 | { | |
85 | "description": "comparison works for very negative numbers", | |
86 | "data": -18446744073709551600, | |
87 | "valid": true | |
88 | } | |
89 | ] | |
90 | }, | |
91 | { | |
92 | "description": "float comparison with high precision on negative numbers", | |
93 | "schema": { | |
94 | "exclusiveMinimum": -972783798187987123879878123.18878137 | |
95 | }, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "comparison works for very negative numbers", | |
99 | "data": -972783798187987123879878123.188781371, | |
100 | "valid": false | |
101 | } | |
102 | ] | |
103 | } | |
104 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of string-encoded content based on media type", | |
3 | "schema": { | |
4 | "contentMediaType": "application/json" | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "a valid JSON document", | |
9 | "data": "{\"foo\": \"bar\"}", | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "an invalid JSON document", | |
14 | "data": "{:}", | |
15 | "valid": false | |
16 | } | |
17 | ] | |
18 | }, | |
19 | { | |
20 | "description": "validation of binary string-encoding", | |
21 | "schema": { | |
22 | "contentEncoding": "base64" | |
23 | }, | |
24 | "tests": [ | |
25 | { | |
26 | "description": "a valid base64 string", | |
27 | "data": "eyJmb28iOiAiYmFyIn0K", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "an invalid base64 string (% is not a valid character)", | |
32 | "data": "eyJmb28iOi%iYmFyIn0K", | |
33 | "valid": false | |
34 | } | |
35 | ] | |
36 | }, | |
37 | { | |
38 | "description": "validation of binary-encoded media type documents", | |
39 | "schema": { | |
40 | "contentMediaType": "application/json", | |
41 | "contentEncoding": "base64" | |
42 | }, | |
43 | "tests": [ | |
44 | { | |
45 | "description": "a valid base64-encoded JSON document", | |
46 | "data": "eyJmb28iOiAiYmFyIn0K", | |
47 | "valid": true | |
48 | }, | |
49 | { | |
50 | "description": "a validly-encoded invalid JSON document", | |
51 | "data": "ezp9Cg==", | |
52 | "valid": false | |
53 | }, | |
54 | { | |
55 | "description": "an invalid base64 string that is valid JSON", | |
56 | "data": "{}", | |
57 | "valid": false | |
58 | } | |
59 | ] | |
60 | } | |
61 | ] |
0 | [ | |
1 | { | |
2 | "description": "ECMA 262 regex non-compliance", | |
3 | "schema": { "format": "regex" }, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "ECMA 262 has no support for \\Z anchor from .NET", | |
7 | "data": "^\\S(|(.|\\n)*\\S)\\Z", | |
8 | "valid": false | |
9 | } | |
10 | ] | |
11 | } | |
12 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of date-time strings", | |
3 | "schema": {"format": "date-time"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid date-time string", | |
7 | "data": "1963-06-19T08:30:06.283185Z", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid date-time string", | |
12 | "data": "06/19/1963 08:30:06 PST", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "only RFC3339 not all of ISO 8601 are valid", | |
17 | "data": "2013-350T01:01:01", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | } | |
22 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of date strings", | |
3 | "schema": {"format": "date"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid date string", | |
7 | "data": "1963-06-19", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid date-time string", | |
12 | "data": "06/19/1963", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "only RFC3339 not all of ISO 8601 are valid", | |
17 | "data": "2013-350", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | } | |
22 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of e-mail addresses", | |
3 | "schema": {"format": "email"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid e-mail address", | |
7 | "data": "joe.bloggs@example.com", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid e-mail address", | |
12 | "data": "2962", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | } | |
17 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of host names", | |
3 | "schema": {"format": "hostname"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid host name", | |
7 | "data": "www.example.com", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid punycoded IDN hostname", | |
12 | "data": "xn--4gbwdl.xn--wgbh1c", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a host name starting with an illegal character", | |
17 | "data": "-a-host-name-that-starts-with--", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "a host name containing illegal characters", | |
22 | "data": "not_a_valid_host_name", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "a host name with a component too long", | |
27 | "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of an internationalized e-mail addresses", | |
3 | "schema": {"format": "idn-email"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid idn e-mail (example@example.test in Hangul)", | |
7 | "data": "실례@실례.테스트", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid idn e-mail address", | |
12 | "data": "2962", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | } | |
17 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of internationalized host names", | |
3 | "schema": {"format": "idn-hostname"}, | |
4 | "disabled" : true, | |
5 | "tests": [ | |
6 | { | |
7 | "description": "a valid host name (example.test in Hangul)", | |
8 | "data": "실례.테스트", | |
9 | "valid": true | |
10 | }, | |
11 | { | |
12 | "description": "illegal first char U+302E Hangul single dot tone mark", | |
13 | "data": "〮실례.테스트", | |
14 | "valid": false | |
15 | }, | |
16 | { | |
17 | "description": "contains illegal char U+302E Hangul single dot tone mark", | |
18 | "data": "실〮례.테스트", | |
19 | "valid": false | |
20 | }, | |
21 | { | |
22 | "description": "a host name with a component too long", | |
23 | "data": "실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실실례례테스트례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례례례례례례례례테스트례례례례례례례례례례례례테스트례례실례.테스트", | |
24 | "valid": false | |
25 | } | |
26 | ] | |
27 | } | |
28 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of IP addresses", | |
3 | "schema": {"format": "ipv4"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid IP address", | |
7 | "data": "192.168.0.1", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an IP address with too many components", | |
12 | "data": "127.0.0.0.1", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "an IP address with out-of-range values", | |
17 | "data": "256.256.256.256", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "an IP address without 4 components", | |
22 | "data": "127.0", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "an IP address as an integer", | |
27 | "data": "0x7f000001", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of IPv6 addresses", | |
3 | "schema": {"format": "ipv6"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid IPv6 address", | |
7 | "data": "::1", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an IPv6 address with out-of-range values", | |
12 | "data": "12345::", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "an IPv6 address with too many components", | |
17 | "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "an IPv6 address containing illegal characters", | |
22 | "data": "::laptop", | |
23 | "valid": false | |
24 | } | |
25 | ] | |
26 | } | |
27 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of IRI References", | |
3 | "schema": {"format": "iri-reference"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid IRI", | |
7 | "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid protocol-relative IRI Reference", | |
12 | "data": "//ƒøø.ßår/?∂éœ=πîx#πîüx", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a valid relative IRI Reference", | |
17 | "data": "/âππ", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "an invalid IRI Reference", | |
22 | "data": "\\\\WINDOWS\\filëßåré", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "a valid IRI Reference", | |
27 | "data": "âππ", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "a valid IRI fragment", | |
32 | "data": "#ƒrägmênt", | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "an invalid IRI fragment", | |
37 | "data": "#ƒräg\\mênt", | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | } | |
42 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of IRIs", | |
3 | "schema": {"format": "iri"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid IRI with anchor tag", | |
7 | "data": "http://ƒøø.ßår/?∂éœ=πîx#πîüx", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid IRI with anchor tag and parantheses", | |
12 | "data": "http://ƒøø.com/blah_(wîkïpédiå)_blah#ßité-1", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a valid IRI with URL-encoded stuff", | |
17 | "data": "http://ƒøø.ßår/?q=Test%20URL-encoded%20stuff", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a valid IRI with many special characters", | |
22 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "a valid IRI based on IPv6", | |
27 | "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "an invalid relative IRI Reference", | |
32 | "data": "/abc", | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "an invalid IRI", | |
37 | "data": "\\\\WINDOWS\\filëßåré", | |
38 | "valid": false | |
39 | }, | |
40 | { | |
41 | "description": "an invalid IRI though valid IRI reference", | |
42 | "data": "âππ", | |
43 | "valid": false | |
44 | } | |
45 | ] | |
46 | } | |
47 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of JSON-pointers (JSON String Representation)", | |
3 | "schema": {"format": "json-pointer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid JSON-pointer", | |
7 | "data": "/foo/bar~0/baz~1/%a", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "not a valid JSON-pointer (~ not escaped)", | |
12 | "data": "/foo/bar~", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "valid JSON-pointer with empty segment", | |
17 | "data": "/foo//bar", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "valid JSON-pointer with the last empty segment", | |
22 | "data": "/foo/bar/", | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "valid JSON-pointer as stated in RFC 6901 #1", | |
27 | "data": "", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "valid JSON-pointer as stated in RFC 6901 #2", | |
32 | "data": "/foo", | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "valid JSON-pointer as stated in RFC 6901 #3", | |
37 | "data": "/foo/0", | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "valid JSON-pointer as stated in RFC 6901 #4", | |
42 | "data": "/", | |
43 | "valid": true | |
44 | }, | |
45 | { | |
46 | "description": "valid JSON-pointer as stated in RFC 6901 #5", | |
47 | "data": "/a~1b", | |
48 | "valid": true | |
49 | }, | |
50 | { | |
51 | "description": "valid JSON-pointer as stated in RFC 6901 #6", | |
52 | "data": "/c%d", | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "valid JSON-pointer as stated in RFC 6901 #7", | |
57 | "data": "/e^f", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "valid JSON-pointer as stated in RFC 6901 #8", | |
62 | "data": "/g|h", | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "valid JSON-pointer as stated in RFC 6901 #9", | |
67 | "data": "/i\\j", | |
68 | "valid": true | |
69 | }, | |
70 | { | |
71 | "description": "valid JSON-pointer as stated in RFC 6901 #10", | |
72 | "data": "/k\"l", | |
73 | "valid": true | |
74 | }, | |
75 | { | |
76 | "description": "valid JSON-pointer as stated in RFC 6901 #11", | |
77 | "data": "/ ", | |
78 | "valid": true | |
79 | }, | |
80 | { | |
81 | "description": "valid JSON-pointer as stated in RFC 6901 #12", | |
82 | "data": "/m~0n", | |
83 | "valid": true | |
84 | }, | |
85 | { | |
86 | "description": "valid JSON-pointer used adding to the last array position", | |
87 | "data": "/foo/-", | |
88 | "valid": true | |
89 | }, | |
90 | { | |
91 | "description": "valid JSON-pointer (- used as object member name)", | |
92 | "data": "/foo/-/bar", | |
93 | "valid": true | |
94 | }, | |
95 | { | |
96 | "description": "valid JSON-pointer (multiple escaped characters)", | |
97 | "data": "/~1~0~0~1~1", | |
98 | "valid": true | |
99 | }, | |
100 | { | |
101 | "description": "valid JSON-pointer (escaped with fraction part) #1", | |
102 | "data": "/~1.1", | |
103 | "valid": true | |
104 | }, | |
105 | { | |
106 | "description": "valid JSON-pointer (escaped with fraction part) #2", | |
107 | "data": "/~0.1", | |
108 | "valid": true | |
109 | }, | |
110 | { | |
111 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #1", | |
112 | "data": "#", | |
113 | "valid": false | |
114 | }, | |
115 | { | |
116 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #2", | |
117 | "data": "#/", | |
118 | "valid": false | |
119 | }, | |
120 | { | |
121 | "description": "not a valid JSON-pointer (URI Fragment Identifier) #3", | |
122 | "data": "#a", | |
123 | "valid": false | |
124 | }, | |
125 | { | |
126 | "description": "not a valid JSON-pointer (some escaped, but not all) #1", | |
127 | "data": "/~0~", | |
128 | "valid": false | |
129 | }, | |
130 | { | |
131 | "description": "not a valid JSON-pointer (some escaped, but not all) #2", | |
132 | "data": "/~0/~", | |
133 | "valid": false | |
134 | }, | |
135 | { | |
136 | "description": "not a valid JSON-pointer (wrong escape character) #1", | |
137 | "data": "/~2", | |
138 | "valid": false | |
139 | }, | |
140 | { | |
141 | "description": "not a valid JSON-pointer (wrong escape character) #2", | |
142 | "data": "/~-1", | |
143 | "valid": false | |
144 | }, | |
145 | { | |
146 | "description": "not a valid JSON-pointer (multiple characters not escaped)", | |
147 | "data": "/~~", | |
148 | "valid": false | |
149 | }, | |
150 | { | |
151 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #1", | |
152 | "data": "a", | |
153 | "valid": false | |
154 | }, | |
155 | { | |
156 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #2", | |
157 | "data": "0", | |
158 | "valid": false | |
159 | }, | |
160 | { | |
161 | "description": "not a valid JSON-pointer (isn't empty nor starts with /) #3", | |
162 | "data": "a/a", | |
163 | "valid": false | |
164 | } | |
165 | ] | |
166 | } | |
167 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of regular expressions", | |
3 | "schema": {"format": "regex"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid regular expression", | |
7 | "data": "([abc])+\\s+$", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a regular expression with unclosed parens is invalid", | |
12 | "data": "^(abc]", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | } | |
17 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of Relative JSON Pointers (RJP)", | |
3 | "schema": {"format": "relative-json-pointer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid upwards RJP", | |
7 | "data": "1", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid downwards RJP", | |
12 | "data": "0/foo/bar", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a valid up and then down RJP, with array index", | |
17 | "data": "2/0/baz/1/zip", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a valid RJP taking the member or index name", | |
22 | "data": "0#", | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "an invalid RJP that is a valid JSON Pointer", | |
27 | "data": "/foo/bar", | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | } | |
32 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of time strings", | |
3 | "schema": {"format": "time"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid time string", | |
7 | "data": "08:30:06.283185Z", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "an invalid time string", | |
12 | "data": "08:30:06 PST", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "only RFC3339 not all of ISO 8601 are valid", | |
17 | "data": "01:01:01,1111", | |
18 | "valid": false | |
19 | } | |
20 | ] | |
21 | } | |
22 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of URI References", | |
3 | "schema": {"format": "uri-reference"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid URI", | |
7 | "data": "http://foo.bar/?baz=qux#quux", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid protocol-relative URI Reference", | |
12 | "data": "//foo.bar/?baz=qux#quux", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a valid relative URI Reference", | |
17 | "data": "/abc", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "an invalid URI Reference", | |
22 | "data": "\\\\WINDOWS\\fileshare", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "a valid URI Reference", | |
27 | "data": "abc", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "a valid URI fragment", | |
32 | "data": "#fragment", | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "an invalid URI fragment", | |
37 | "data": "#frag\\ment", | |
38 | "valid": false | |
39 | } | |
40 | ] | |
41 | } | |
42 | ] |
0 | [ | |
1 | { | |
2 | "description": "format: uri-template", | |
3 | "schema": { | |
4 | "format": "uri-template" | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "a valid uri-template", | |
9 | "data": "http://example.com/dictionary/{term:1}/{term}", | |
10 | "valid": true | |
11 | }, | |
12 | { | |
13 | "description": "an invalid uri-template", | |
14 | "data": "http://example.com/dictionary/{term:1}/{term", | |
15 | "valid": false | |
16 | }, | |
17 | { | |
18 | "description": "a valid uri-template without variables", | |
19 | "data": "http://example.com/dictionary", | |
20 | "valid": true | |
21 | }, | |
22 | { | |
23 | "description": "a valid relative uri-template", | |
24 | "data": "dictionary/{term:1}/{term}", | |
25 | "valid": true | |
26 | } | |
27 | ] | |
28 | } | |
29 | ] |
0 | [ | |
1 | { | |
2 | "description": "validation of URIs", | |
3 | "schema": {"format": "uri"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a valid URL with anchor tag", | |
7 | "data": "http://foo.bar/?baz=qux#quux", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a valid URL with anchor tag and parantheses", | |
12 | "data": "http://foo.com/blah_(wikipedia)_blah#cite-1", | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "a valid URL with URL-encoded stuff", | |
17 | "data": "http://foo.bar/?q=Test%20URL-encoded%20stuff", | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a valid puny-coded URL ", | |
22 | "data": "http://xn--nw2a.xn--j6w193g/", | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "a valid URL with many special characters", | |
27 | "data": "http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com", | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "a valid URL based on IPv4", | |
32 | "data": "http://223.255.255.254", | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "a valid URL with ftp scheme", | |
37 | "data": "ftp://ftp.is.co.za/rfc/rfc1808.txt", | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "a valid URL for a simple text file", | |
42 | "data": "http://www.ietf.org/rfc/rfc2396.txt", | |
43 | "valid": true | |
44 | }, | |
45 | { | |
46 | "description": "a valid URL ", | |
47 | "data": "ldap://[2001:db8::7]/c=GB?objectClass?one", | |
48 | "valid": true | |
49 | }, | |
50 | { | |
51 | "description": "a valid mailto URI", | |
52 | "data": "mailto:John.Doe@example.com", | |
53 | "valid": true | |
54 | }, | |
55 | { | |
56 | "description": "a valid newsgroup URI", | |
57 | "data": "news:comp.infosystems.www.servers.unix", | |
58 | "valid": true | |
59 | }, | |
60 | { | |
61 | "description": "a valid tel URI", | |
62 | "data": "tel:+1-816-555-1212", | |
63 | "valid": true | |
64 | }, | |
65 | { | |
66 | "description": "a valid URN", | |
67 | "data": "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", | |
68 | "valid": true | |
69 | }, | |
70 | { | |
71 | "description": "an invalid protocol-relative URI Reference", | |
72 | "data": "//foo.bar/?baz=qux#quux", | |
73 | "valid": false | |
74 | }, | |
75 | { | |
76 | "description": "an invalid relative URI Reference", | |
77 | "data": "/abc", | |
78 | "valid": false | |
79 | }, | |
80 | { | |
81 | "description": "an invalid URI", | |
82 | "data": "\\\\WINDOWS\\fileshare", | |
83 | "valid": false | |
84 | }, | |
85 | { | |
86 | "description": "an invalid URI though valid URI reference", | |
87 | "data": "abc", | |
88 | "valid": false | |
89 | }, | |
90 | { | |
91 | "description": "an invalid URI with spaces", | |
92 | "data": "http:// shouldfail.com", | |
93 | "valid": false | |
94 | }, | |
95 | { | |
96 | "description": "an invalid URI with spaces and missing scheme", | |
97 | "data": ":// should fail", | |
98 | "valid": false | |
99 | } | |
100 | ] | |
101 | } | |
102 | ] |
0 | [ | |
1 | { | |
2 | "description": "some languages do not distinguish between different types of numeric value", | |
3 | "schema": { | |
4 | "type": "integer" | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "a float without fractional part is an integer", | |
9 | "data": 1.0, | |
10 | "valid": true | |
11 | } | |
12 | ] | |
13 | } | |
14 | ] |
0 | [ | |
1 | { | |
2 | "description": "pattern validation", | |
3 | "schema": {"pattern": "^a*$"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "a matching pattern is valid", | |
7 | "data": "aaa", | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a non-matching pattern is invalid", | |
12 | "data": "abc", | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "ignores non-strings", | |
17 | "data": true, | |
18 | "valid": true | |
19 | } | |
20 | ] | |
21 | }, | |
22 | { | |
23 | "description": "pattern is not anchored", | |
24 | "schema": {"pattern": "a+"}, | |
25 | "tests": [ | |
26 | { | |
27 | "description": "matches a substring", | |
28 | "data": "xxaayy", | |
29 | "valid": true | |
30 | } | |
31 | ] | |
32 | } | |
33 | ] |
0 | [ | |
1 | { | |
2 | "description": | |
3 | "patternProperties validates properties matching a regex", | |
4 | "schema": { | |
5 | "patternProperties": { | |
6 | "f.*o": {"type": "integer"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "a single valid match is valid", | |
12 | "data": {"foo": 1}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "multiple valid matches is valid", | |
17 | "data": {"foo": 1, "foooooo" : 2}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "a single invalid match is invalid", | |
22 | "data": {"foo": "bar", "fooooo": 2}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "multiple invalid matches is invalid", | |
27 | "data": {"foo": "bar", "foooooo" : "baz"}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": ["foo"], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores strings", | |
37 | "data": "foo", | |
38 | "valid": true | |
39 | }, | |
40 | { | |
41 | "description": "ignores other non-objects", | |
42 | "data": 12, | |
43 | "valid": true | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "multiple simultaneous patternProperties are validated", | |
49 | "schema": { | |
50 | "patternProperties": { | |
51 | "a*": {"type": "integer"}, | |
52 | "aaa*": {"maximum": 20} | |
53 | } | |
54 | }, | |
55 | "tests": [ | |
56 | { | |
57 | "description": "a single valid match is valid", | |
58 | "data": {"a": 21}, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a simultaneous match is valid", | |
63 | "data": {"aaaa": 18}, | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "multiple matches is valid", | |
68 | "data": {"a": 21, "aaaa": 18}, | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "an invalid due to one is invalid", | |
73 | "data": {"a": "bar"}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an invalid due to the other is invalid", | |
78 | "data": {"aaaa": 31}, | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "an invalid due to both is invalid", | |
83 | "data": {"aaa": "foo", "aaaa": 31}, | |
84 | "valid": false | |
85 | } | |
86 | ] | |
87 | }, | |
88 | { | |
89 | "description": "regexes are not anchored by default and are case sensitive", | |
90 | "schema": { | |
91 | "patternProperties": { | |
92 | "[0-9]{2,}": { "type": "boolean" }, | |
93 | "X_": { "type": "string" } | |
94 | } | |
95 | }, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "non recognized members are ignored", | |
99 | "data": { "answer 1": "42" }, | |
100 | "valid": true | |
101 | }, | |
102 | { | |
103 | "description": "recognized members are accounted for", | |
104 | "data": { "a31b": null }, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "regexes are case sensitive", | |
109 | "data": { "a_x_3": 3 }, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "regexes are case sensitive, 2", | |
114 | "data": { "a_X_3": 3 }, | |
115 | "valid": false | |
116 | } | |
117 | ] | |
118 | }, | |
119 | { | |
120 | "description": "patternProperties with boolean schemas", | |
121 | "schema": { | |
122 | "patternProperties": { | |
123 | "f.*": true, | |
124 | "b.*": false | |
125 | } | |
126 | }, | |
127 | "tests": [ | |
128 | { | |
129 | "description": "object with property matching schema true is valid", | |
130 | "data": {"foo": 1}, | |
131 | "valid": true | |
132 | }, | |
133 | { | |
134 | "description": "object with property matching schema false is invalid", | |
135 | "data": {"bar": 2}, | |
136 | "valid": false | |
137 | }, | |
138 | { | |
139 | "description": "object with both properties is invalid", | |
140 | "data": {"foo": 1, "bar": 2}, | |
141 | "valid": false | |
142 | }, | |
143 | { | |
144 | "description": "empty object is valid", | |
145 | "data": {}, | |
146 | "valid": true | |
147 | } | |
148 | ] | |
149 | } | |
150 | ] |
0 | [ | |
1 | { | |
2 | "description": "object properties validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"type": "integer"}, | |
6 | "bar": {"type": "string"} | |
7 | } | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "both properties present and valid is valid", | |
12 | "data": {"foo": 1, "bar": "baz"}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "one property invalid is invalid", | |
17 | "data": {"foo": 1, "bar": {}}, | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "both properties invalid is invalid", | |
22 | "data": {"foo": [], "bar": {}}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "doesn't invalidate other properties", | |
27 | "data": {"quux": []}, | |
28 | "valid": true | |
29 | }, | |
30 | { | |
31 | "description": "ignores arrays", | |
32 | "data": [], | |
33 | "valid": true | |
34 | }, | |
35 | { | |
36 | "description": "ignores other non-objects", | |
37 | "data": 12, | |
38 | "valid": true | |
39 | } | |
40 | ] | |
41 | }, | |
42 | { | |
43 | "description": | |
44 | "properties, patternProperties, additionalProperties interaction", | |
45 | "schema": { | |
46 | "properties": { | |
47 | "foo": {"type": "array", "maxItems": 3}, | |
48 | "bar": {"type": "array"} | |
49 | }, | |
50 | "patternProperties": {"f.o": {"minItems": 2}}, | |
51 | "additionalProperties": {"type": "integer"} | |
52 | }, | |
53 | "tests": [ | |
54 | { | |
55 | "description": "property validates property", | |
56 | "data": {"foo": [1, 2]}, | |
57 | "valid": true | |
58 | }, | |
59 | { | |
60 | "description": "property invalidates property", | |
61 | "data": {"foo": [1, 2, 3, 4]}, | |
62 | "valid": false | |
63 | }, | |
64 | { | |
65 | "description": "patternProperty invalidates property", | |
66 | "data": {"foo": []}, | |
67 | "valid": false | |
68 | }, | |
69 | { | |
70 | "description": "patternProperty validates nonproperty", | |
71 | "data": {"fxo": [1, 2]}, | |
72 | "valid": true | |
73 | }, | |
74 | { | |
75 | "description": "patternProperty invalidates nonproperty", | |
76 | "data": {"fxo": []}, | |
77 | "valid": false | |
78 | }, | |
79 | { | |
80 | "description": "additionalProperty ignores property", | |
81 | "data": {"bar": []}, | |
82 | "valid": true | |
83 | }, | |
84 | { | |
85 | "description": "additionalProperty validates others", | |
86 | "data": {"quux": 3}, | |
87 | "valid": true | |
88 | }, | |
89 | { | |
90 | "description": "additionalProperty invalidates others", | |
91 | "data": {"quux": "foo"}, | |
92 | "valid": false | |
93 | } | |
94 | ] | |
95 | }, | |
96 | { | |
97 | "description": "properties with boolean schema", | |
98 | "schema": { | |
99 | "properties": { | |
100 | "foo": true, | |
101 | "bar": false | |
102 | } | |
103 | }, | |
104 | "tests": [ | |
105 | { | |
106 | "description": "no property present is valid", | |
107 | "data": {}, | |
108 | "valid": true | |
109 | }, | |
110 | { | |
111 | "description": "only 'true' property present is valid", | |
112 | "data": {"foo": 1}, | |
113 | "valid": true | |
114 | }, | |
115 | { | |
116 | "description": "only 'false' property present is invalid", | |
117 | "data": {"bar": 2}, | |
118 | "valid": false | |
119 | }, | |
120 | { | |
121 | "description": "both properties present is invalid", | |
122 | "data": {"foo": 1, "bar": 2}, | |
123 | "valid": false | |
124 | } | |
125 | ] | |
126 | } | |
127 | ] |
0 | [ | |
1 | { | |
2 | "description": "propertyNames validation", | |
3 | "schema": { | |
4 | "propertyNames": {"maxLength": 3} | |
5 | }, | |
6 | "tests": [ | |
7 | { | |
8 | "description": "all property names valid", | |
9 | "data": { | |
10 | "f": {}, | |
11 | "foo": {} | |
12 | }, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "some property names invalid", | |
17 | "data": { | |
18 | "foo": {}, | |
19 | "foobar": {} | |
20 | }, | |
21 | "valid": false | |
22 | }, | |
23 | { | |
24 | "description": "object without properties is valid", | |
25 | "data": {}, | |
26 | "valid": true | |
27 | }, | |
28 | { | |
29 | "description": "ignores arrays", | |
30 | "data": [1, 2, 3, 4], | |
31 | "valid": true | |
32 | }, | |
33 | { | |
34 | "description": "ignores strings", | |
35 | "data": "foobar", | |
36 | "valid": true | |
37 | }, | |
38 | { | |
39 | "description": "ignores other non-objects", | |
40 | "data": 12, | |
41 | "valid": true | |
42 | } | |
43 | ] | |
44 | }, | |
45 | { | |
46 | "description": "propertyNames with boolean schema true", | |
47 | "schema": {"propertyNames": true}, | |
48 | "tests": [ | |
49 | { | |
50 | "description": "object with any properties is valid", | |
51 | "data": {"foo": 1}, | |
52 | "valid": true | |
53 | }, | |
54 | { | |
55 | "description": "empty object is valid", | |
56 | "data": {}, | |
57 | "valid": true | |
58 | } | |
59 | ] | |
60 | }, | |
61 | { | |
62 | "description": "propertyNames with boolean schema false", | |
63 | "schema": {"propertyNames": false}, | |
64 | "tests": [ | |
65 | { | |
66 | "description": "object with any properties is invalid", | |
67 | "data": {"foo": 1}, | |
68 | "valid": false | |
69 | }, | |
70 | { | |
71 | "description": "empty object is valid", | |
72 | "data": {}, | |
73 | "valid": true | |
74 | } | |
75 | ] | |
76 | } | |
77 | ] |
0 | [ | |
1 | { | |
2 | "description": "root pointer ref", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {"$ref": "#"} | |
6 | }, | |
7 | "additionalProperties": false | |
8 | }, | |
9 | "tests": [ | |
10 | { | |
11 | "description": "match", | |
12 | "data": {"foo": false}, | |
13 | "valid": true | |
14 | }, | |
15 | { | |
16 | "description": "recursive match", | |
17 | "data": {"foo": {"foo": false}}, | |
18 | "valid": true | |
19 | }, | |
20 | { | |
21 | "description": "mismatch", | |
22 | "data": {"bar": false}, | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "recursive mismatch", | |
27 | "data": {"foo": {"bar": false}}, | |
28 | "valid": false | |
29 | } | |
30 | ] | |
31 | }, | |
32 | { | |
33 | "description": "relative pointer ref to object", | |
34 | "schema": { | |
35 | "properties": { | |
36 | "foo": {"type": "integer"}, | |
37 | "bar": {"$ref": "#/properties/foo"} | |
38 | } | |
39 | }, | |
40 | "tests": [ | |
41 | { | |
42 | "description": "match", | |
43 | "data": {"bar": 3}, | |
44 | "valid": true | |
45 | }, | |
46 | { | |
47 | "description": "mismatch", | |
48 | "data": {"bar": true}, | |
49 | "valid": false | |
50 | } | |
51 | ] | |
52 | }, | |
53 | { | |
54 | "description": "relative pointer ref to array", | |
55 | "schema": { | |
56 | "items": [ | |
57 | {"type": "integer"}, | |
58 | {"$ref": "#/items/0"} | |
59 | ] | |
60 | }, | |
61 | "tests": [ | |
62 | { | |
63 | "description": "match array", | |
64 | "data": [1, 2], | |
65 | "valid": true | |
66 | }, | |
67 | { | |
68 | "description": "mismatch array", | |
69 | "data": [1, "foo"], | |
70 | "valid": false | |
71 | } | |
72 | ] | |
73 | }, | |
74 | { | |
75 | "description": "escaped pointer ref", | |
76 | "schema": { | |
77 | "tilda~field": {"type": "integer"}, | |
78 | "slash/field": {"type": "integer"}, | |
79 | "percent%field": {"type": "integer"}, | |
80 | "properties": { | |
81 | "tilda": {"$ref": "#/tilda~0field"}, | |
82 | "slash": {"$ref": "#/slash~1field"}, | |
83 | "percent": {"$ref": "#/percent%25field"} | |
84 | } | |
85 | }, | |
86 | "tests": [ | |
87 | { | |
88 | "description": "slash invalid", | |
89 | "data": {"slash": "aoeu"}, | |
90 | "valid": false | |
91 | }, | |
92 | { | |
93 | "description": "tilda invalid", | |
94 | "data": {"tilda": "aoeu"}, | |
95 | "valid": false | |
96 | }, | |
97 | { | |
98 | "description": "percent invalid", | |
99 | "data": {"percent": "aoeu"}, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "slash valid", | |
104 | "data": {"slash": 123}, | |
105 | "valid": true | |
106 | }, | |
107 | { | |
108 | "description": "tilda valid", | |
109 | "data": {"tilda": 123}, | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "percent valid", | |
114 | "data": {"percent": 123}, | |
115 | "valid": true | |
116 | } | |
117 | ] | |
118 | }, | |
119 | { | |
120 | "description": "nested refs", | |
121 | "schema": { | |
122 | "definitions": { | |
123 | "a": {"type": "integer"}, | |
124 | "b": {"$ref": "#/definitions/a"}, | |
125 | "c": {"$ref": "#/definitions/b"} | |
126 | }, | |
127 | "$ref": "#/definitions/c" | |
128 | }, | |
129 | "tests": [ | |
130 | { | |
131 | "description": "nested ref valid", | |
132 | "data": 5, | |
133 | "valid": true | |
134 | }, | |
135 | { | |
136 | "description": "nested ref invalid", | |
137 | "data": "a", | |
138 | "valid": false | |
139 | } | |
140 | ] | |
141 | }, | |
142 | { | |
143 | "description": "ref overrides any sibling keywords", | |
144 | "schema": { | |
145 | "definitions": { | |
146 | "reffed": { | |
147 | "type": "array" | |
148 | } | |
149 | }, | |
150 | "properties": { | |
151 | "foo": { | |
152 | "$ref": "#/definitions/reffed", | |
153 | "maxItems": 2 | |
154 | } | |
155 | } | |
156 | }, | |
157 | "tests": [ | |
158 | { | |
159 | "description": "ref valid", | |
160 | "data": { "foo": [] }, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "ref valid, maxItems ignored", | |
165 | "data": { "foo": [ 1, 2, 3] }, | |
166 | "valid": true | |
167 | }, | |
168 | { | |
169 | "description": "ref invalid", | |
170 | "data": { "foo": "string" }, | |
171 | "valid": false | |
172 | } | |
173 | ] | |
174 | }, | |
175 | { | |
176 | "description": "remote ref, containing refs itself", | |
177 | "schema": {"$ref": "http://json-schema.org/draft-07/schema#"}, | |
178 | "tests": [ | |
179 | { | |
180 | "description": "remote ref valid", | |
181 | "data": {"minLength": 1}, | |
182 | "valid": true | |
183 | }, | |
184 | { | |
185 | "description": "remote ref invalid", | |
186 | "data": {"minLength": -1}, | |
187 | "valid": false | |
188 | } | |
189 | ] | |
190 | }, | |
191 | { | |
192 | "description": "property named $ref that is not a reference", | |
193 | "schema": { | |
194 | "properties": { | |
195 | "$ref": {"type": "string"} | |
196 | } | |
197 | }, | |
198 | "tests": [ | |
199 | { | |
200 | "description": "property named $ref valid", | |
201 | "data": {"$ref": "a"}, | |
202 | "valid": true | |
203 | }, | |
204 | { | |
205 | "description": "property named $ref invalid", | |
206 | "data": {"$ref": 2}, | |
207 | "valid": false | |
208 | } | |
209 | ] | |
210 | }, | |
211 | { | |
212 | "description": "$ref to boolean schema true", | |
213 | "schema": { | |
214 | "$ref": "#/definitions/bool", | |
215 | "definitions": { | |
216 | "bool": true | |
217 | } | |
218 | }, | |
219 | "tests": [ | |
220 | { | |
221 | "description": "any value is valid", | |
222 | "data": "foo", | |
223 | "valid": true | |
224 | } | |
225 | ] | |
226 | }, | |
227 | { | |
228 | "description": "$ref to boolean schema false", | |
229 | "schema": { | |
230 | "$ref": "#/definitions/bool", | |
231 | "definitions": { | |
232 | "bool": false | |
233 | } | |
234 | }, | |
235 | "tests": [ | |
236 | { | |
237 | "description": "any value is invalid", | |
238 | "data": "foo", | |
239 | "valid": false | |
240 | } | |
241 | ] | |
242 | }, | |
243 | { | |
244 | "description": "Recursive references between schemas", | |
245 | "schema": { | |
246 | "$id": "http://localhost:1234/tree", | |
247 | "description": "tree of nodes", | |
248 | "type": "object", | |
249 | "properties": { | |
250 | "meta": {"type": "string"}, | |
251 | "nodes": { | |
252 | "type": "array", | |
253 | "items": {"$ref": "node"} | |
254 | } | |
255 | }, | |
256 | "required": ["meta", "nodes"], | |
257 | "definitions": { | |
258 | "node": { | |
259 | "$id": "http://localhost:1234/node", | |
260 | "description": "node", | |
261 | "type": "object", | |
262 | "properties": { | |
263 | "value": {"type": "number"}, | |
264 | "subtree": {"$ref": "tree"} | |
265 | }, | |
266 | "required": ["value"] | |
267 | } | |
268 | } | |
269 | }, | |
270 | "tests": [ | |
271 | { | |
272 | "description": "valid tree", | |
273 | "data": { | |
274 | "meta": "root", | |
275 | "nodes": [ | |
276 | { | |
277 | "value": 1, | |
278 | "subtree": { | |
279 | "meta": "child", | |
280 | "nodes": [ | |
281 | {"value": 1.1}, | |
282 | {"value": 1.2} | |
283 | ] | |
284 | } | |
285 | }, | |
286 | { | |
287 | "value": 2, | |
288 | "subtree": { | |
289 | "meta": "child", | |
290 | "nodes": [ | |
291 | {"value": 2.1}, | |
292 | {"value": 2.2} | |
293 | ] | |
294 | } | |
295 | } | |
296 | ] | |
297 | }, | |
298 | "valid": true | |
299 | }, | |
300 | { | |
301 | "description": "invalid tree", | |
302 | "data": { | |
303 | "meta": "root", | |
304 | "nodes": [ | |
305 | { | |
306 | "value": 1, | |
307 | "subtree": { | |
308 | "meta": "child", | |
309 | "nodes": [ | |
310 | {"value": "string is invalid"}, | |
311 | {"value": 1.2} | |
312 | ] | |
313 | } | |
314 | }, | |
315 | { | |
316 | "value": 2, | |
317 | "subtree": { | |
318 | "meta": "child", | |
319 | "nodes": [ | |
320 | {"value": 2.1}, | |
321 | {"value": 2.2} | |
322 | ] | |
323 | } | |
324 | } | |
325 | ] | |
326 | }, | |
327 | "valid": false | |
328 | } | |
329 | ] | |
330 | } | |
331 | ] |
0 | [ | |
1 | { | |
2 | "description": "remote ref", | |
3 | "schema": {"$ref": "http://localhost:1234/integer.json"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "remote ref valid", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "remote ref invalid", | |
12 | "data": "a", | |
13 | "valid": false | |
14 | } | |
15 | ] | |
16 | }, | |
17 | { | |
18 | "description": "fragment within remote ref", | |
19 | "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, | |
20 | "tests": [ | |
21 | { | |
22 | "description": "remote fragment valid", | |
23 | "data": 1, | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "remote fragment invalid", | |
28 | "data": "a", | |
29 | "valid": false | |
30 | } | |
31 | ] | |
32 | }, | |
33 | { | |
34 | "description": "ref within remote ref", | |
35 | "schema": { | |
36 | "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" | |
37 | }, | |
38 | "tests": [ | |
39 | { | |
40 | "description": "ref within ref valid", | |
41 | "data": 1, | |
42 | "valid": true | |
43 | }, | |
44 | { | |
45 | "description": "ref within ref invalid", | |
46 | "data": "a", | |
47 | "valid": false | |
48 | } | |
49 | ] | |
50 | }, | |
51 | { | |
52 | "description": "base URI change", | |
53 | "schema": { | |
54 | "$id": "http://localhost:1234/", | |
55 | "items": { | |
56 | "$id": "folder/", | |
57 | "items": {"$ref": "folderInteger.json"} | |
58 | } | |
59 | }, | |
60 | "tests": [ | |
61 | { | |
62 | "description": "base URI change ref valid", | |
63 | "data": [[1]], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "base URI change ref invalid", | |
68 | "data": [["a"]], | |
69 | "valid": false | |
70 | } | |
71 | ] | |
72 | }, | |
73 | { | |
74 | "description": "base URI change - change folder", | |
75 | "schema": { | |
76 | "$id": "http://localhost:1234/scope_change_defs1.json", | |
77 | "type" : "object", | |
78 | "properties": { | |
79 | "list": {"$ref": "#/definitions/baz"} | |
80 | }, | |
81 | "definitions": { | |
82 | "baz": { | |
83 | "$id": "folder/", | |
84 | "type": "array", | |
85 | "items": {"$ref": "folderInteger.json"} | |
86 | } | |
87 | } | |
88 | }, | |
89 | "tests": [ | |
90 | { | |
91 | "description": "number is valid", | |
92 | "data": {"list": [1]}, | |
93 | "valid": true | |
94 | }, | |
95 | { | |
96 | "description": "string is invalid", | |
97 | "data": {"list": ["a"]}, | |
98 | "valid": false | |
99 | } | |
100 | ] | |
101 | }, | |
102 | { | |
103 | "description": "base URI change - change folder in subschema", | |
104 | "schema": { | |
105 | "$id": "http://localhost:1234/scope_change_defs2.json", | |
106 | "type" : "object", | |
107 | "properties": { | |
108 | "list": {"$ref": "#/definitions/baz/definitions/bar"} | |
109 | }, | |
110 | "definitions": { | |
111 | "baz": { | |
112 | "$id": "folder/", | |
113 | "definitions": { | |
114 | "bar": { | |
115 | "type": "array", | |
116 | "items": {"$ref": "folderInteger.json"} | |
117 | } | |
118 | } | |
119 | } | |
120 | } | |
121 | }, | |
122 | "tests": [ | |
123 | { | |
124 | "description": "number is valid", | |
125 | "data": {"list": [1]}, | |
126 | "valid": true | |
127 | }, | |
128 | { | |
129 | "description": "string is invalid", | |
130 | "data": {"list": ["a"]}, | |
131 | "valid": false | |
132 | } | |
133 | ] | |
134 | }, | |
135 | { | |
136 | "description": "root ref in remote ref", | |
137 | "schema": { | |
138 | "$id": "http://localhost:1234/object", | |
139 | "type": "object", | |
140 | "properties": { | |
141 | "name": {"$ref": "name.json#/definitions/orNull"} | |
142 | } | |
143 | }, | |
144 | "tests": [ | |
145 | { | |
146 | "description": "string is valid", | |
147 | "data": { | |
148 | "name": "foo" | |
149 | }, | |
150 | "valid": true | |
151 | }, | |
152 | { | |
153 | "description": "null is valid", | |
154 | "data": { | |
155 | "name": null | |
156 | }, | |
157 | "valid": true | |
158 | }, | |
159 | { | |
160 | "description": "object is invalid", | |
161 | "data": { | |
162 | "name": { | |
163 | "name": null | |
164 | } | |
165 | }, | |
166 | "valid": false | |
167 | } | |
168 | ] | |
169 | } | |
170 | ] |
0 | [ | |
1 | { | |
2 | "description": "required validation", | |
3 | "schema": { | |
4 | "properties": { | |
5 | "foo": {}, | |
6 | "bar": {} | |
7 | }, | |
8 | "required": ["foo"] | |
9 | }, | |
10 | "tests": [ | |
11 | { | |
12 | "description": "present required property is valid", | |
13 | "data": {"foo": 1}, | |
14 | "valid": true | |
15 | }, | |
16 | { | |
17 | "description": "non-present required property is invalid", | |
18 | "data": {"bar": 1}, | |
19 | "valid": false | |
20 | }, | |
21 | { | |
22 | "description": "ignores arrays", | |
23 | "data": [], | |
24 | "valid": true | |
25 | }, | |
26 | { | |
27 | "description": "ignores strings", | |
28 | "data": "", | |
29 | "valid": true | |
30 | }, | |
31 | { | |
32 | "description": "ignores other non-objects", | |
33 | "data": 12, | |
34 | "valid": true | |
35 | } | |
36 | ] | |
37 | }, | |
38 | { | |
39 | "description": "required default validation", | |
40 | "schema": { | |
41 | "properties": { | |
42 | "foo": {} | |
43 | } | |
44 | }, | |
45 | "tests": [ | |
46 | { | |
47 | "description": "not required by default", | |
48 | "data": {}, | |
49 | "valid": true | |
50 | } | |
51 | ] | |
52 | }, | |
53 | { | |
54 | "description": "required with empty array", | |
55 | "schema": { | |
56 | "properties": { | |
57 | "foo": {} | |
58 | }, | |
59 | "required": [] | |
60 | }, | |
61 | "tests": [ | |
62 | { | |
63 | "description": "property not required", | |
64 | "data": {}, | |
65 | "valid": true | |
66 | } | |
67 | ] | |
68 | } | |
69 | ] |
0 | [ | |
1 | { | |
2 | "description": "integer type matches integers", | |
3 | "schema": {"type": "integer"}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "an integer is an integer", | |
7 | "data": 1, | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "a float is not an integer", | |
12 | "data": 1.1, | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "a string is not an integer", | |
17 | "data": "foo", | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "a string is still not an integer, even if it looks like one", | |
22 | "data": "1", | |
23 | "valid": false | |
24 | }, | |
25 | { | |
26 | "description": "an object is not an integer", | |
27 | "data": {}, | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "an array is not an integer", | |
32 | "data": [], | |
33 | "valid": false | |
34 | }, | |
35 | { | |
36 | "description": "a boolean is not an integer", | |
37 | "data": true, | |
38 | "valid": false | |
39 | }, | |
40 | { | |
41 | "description": "null is not an integer", | |
42 | "data": null, | |
43 | "valid": false | |
44 | } | |
45 | ] | |
46 | }, | |
47 | { | |
48 | "description": "number type matches numbers", | |
49 | "schema": {"type": "number"}, | |
50 | "tests": [ | |
51 | { | |
52 | "description": "an integer is a number", | |
53 | "data": 1, | |
54 | "valid": true | |
55 | }, | |
56 | { | |
57 | "description": "a float is a number", | |
58 | "data": 1.1, | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "a string is not a number", | |
63 | "data": "foo", | |
64 | "valid": false | |
65 | }, | |
66 | { | |
67 | "description": "a string is still not a number, even if it looks like one", | |
68 | "data": "1", | |
69 | "valid": false | |
70 | }, | |
71 | { | |
72 | "description": "an object is not a number", | |
73 | "data": {}, | |
74 | "valid": false | |
75 | }, | |
76 | { | |
77 | "description": "an array is not a number", | |
78 | "data": [], | |
79 | "valid": false | |
80 | }, | |
81 | { | |
82 | "description": "a boolean is not a number", | |
83 | "data": true, | |
84 | "valid": false | |
85 | }, | |
86 | { | |
87 | "description": "null is not a number", | |
88 | "data": null, | |
89 | "valid": false | |
90 | } | |
91 | ] | |
92 | }, | |
93 | { | |
94 | "description": "string type matches strings", | |
95 | "schema": {"type": "string"}, | |
96 | "tests": [ | |
97 | { | |
98 | "description": "1 is not a string", | |
99 | "data": 1, | |
100 | "valid": false | |
101 | }, | |
102 | { | |
103 | "description": "a float is not a string", | |
104 | "data": 1.1, | |
105 | "valid": false | |
106 | }, | |
107 | { | |
108 | "description": "a string is a string", | |
109 | "data": "foo", | |
110 | "valid": true | |
111 | }, | |
112 | { | |
113 | "description": "a string is still a string, even if it looks like a number", | |
114 | "data": "1", | |
115 | "valid": true | |
116 | }, | |
117 | { | |
118 | "description": "an object is not a string", | |
119 | "data": {}, | |
120 | "valid": false | |
121 | }, | |
122 | { | |
123 | "description": "an array is not a string", | |
124 | "data": [], | |
125 | "valid": false | |
126 | }, | |
127 | { | |
128 | "description": "a boolean is not a string", | |
129 | "data": true, | |
130 | "valid": false | |
131 | }, | |
132 | { | |
133 | "description": "null is not a string", | |
134 | "data": null, | |
135 | "valid": false | |
136 | } | |
137 | ] | |
138 | }, | |
139 | { | |
140 | "description": "object type matches objects", | |
141 | "schema": {"type": "object"}, | |
142 | "tests": [ | |
143 | { | |
144 | "description": "an integer is not an object", | |
145 | "data": 1, | |
146 | "valid": false | |
147 | }, | |
148 | { | |
149 | "description": "a float is not an object", | |
150 | "data": 1.1, | |
151 | "valid": false | |
152 | }, | |
153 | { | |
154 | "description": "a string is not an object", | |
155 | "data": "foo", | |
156 | "valid": false | |
157 | }, | |
158 | { | |
159 | "description": "an object is an object", | |
160 | "data": {}, | |
161 | "valid": true | |
162 | }, | |
163 | { | |
164 | "description": "an array is not an object", | |
165 | "data": [], | |
166 | "valid": false | |
167 | }, | |
168 | { | |
169 | "description": "a boolean is not an object", | |
170 | "data": true, | |
171 | "valid": false | |
172 | }, | |
173 | { | |
174 | "description": "null is not an object", | |
175 | "data": null, | |
176 | "valid": false | |
177 | } | |
178 | ] | |
179 | }, | |
180 | { | |
181 | "description": "array type matches arrays", | |
182 | "schema": {"type": "array"}, | |
183 | "tests": [ | |
184 | { | |
185 | "description": "an integer is not an array", | |
186 | "data": 1, | |
187 | "valid": false | |
188 | }, | |
189 | { | |
190 | "description": "a float is not an array", | |
191 | "data": 1.1, | |
192 | "valid": false | |
193 | }, | |
194 | { | |
195 | "description": "a string is not an array", | |
196 | "data": "foo", | |
197 | "valid": false | |
198 | }, | |
199 | { | |
200 | "description": "an object is not an array", | |
201 | "data": {}, | |
202 | "valid": false | |
203 | }, | |
204 | { | |
205 | "description": "an array is an array", | |
206 | "data": [], | |
207 | "valid": true | |
208 | }, | |
209 | { | |
210 | "description": "a boolean is not an array", | |
211 | "data": true, | |
212 | "valid": false | |
213 | }, | |
214 | { | |
215 | "description": "null is not an array", | |
216 | "data": null, | |
217 | "valid": false | |
218 | } | |
219 | ] | |
220 | }, | |
221 | { | |
222 | "description": "boolean type matches booleans", | |
223 | "schema": {"type": "boolean"}, | |
224 | "tests": [ | |
225 | { | |
226 | "description": "an integer is not a boolean", | |
227 | "data": 1, | |
228 | "valid": false | |
229 | }, | |
230 | { | |
231 | "description": "a float is not a boolean", | |
232 | "data": 1.1, | |
233 | "valid": false | |
234 | }, | |
235 | { | |
236 | "description": "a string is not a boolean", | |
237 | "data": "foo", | |
238 | "valid": false | |
239 | }, | |
240 | { | |
241 | "description": "an object is not a boolean", | |
242 | "data": {}, | |
243 | "valid": false | |
244 | }, | |
245 | { | |
246 | "description": "an array is not a boolean", | |
247 | "data": [], | |
248 | "valid": false | |
249 | }, | |
250 | { | |
251 | "description": "a boolean is a boolean", | |
252 | "data": true, | |
253 | "valid": true | |
254 | }, | |
255 | { | |
256 | "description": "null is not a boolean", | |
257 | "data": null, | |
258 | "valid": false | |
259 | } | |
260 | ] | |
261 | }, | |
262 | { | |
263 | "description": "null type matches only the null object", | |
264 | "schema": {"type": "null"}, | |
265 | "tests": [ | |
266 | { | |
267 | "description": "an integer is not null", | |
268 | "data": 1, | |
269 | "valid": false | |
270 | }, | |
271 | { | |
272 | "description": "a float is not null", | |
273 | "data": 1.1, | |
274 | "valid": false | |
275 | }, | |
276 | { | |
277 | "description": "a string is not null", | |
278 | "data": "foo", | |
279 | "valid": false | |
280 | }, | |
281 | { | |
282 | "description": "an object is not null", | |
283 | "data": {}, | |
284 | "valid": false | |
285 | }, | |
286 | { | |
287 | "description": "an array is not null", | |
288 | "data": [], | |
289 | "valid": false | |
290 | }, | |
291 | { | |
292 | "description": "a boolean is not null", | |
293 | "data": true, | |
294 | "valid": false | |
295 | }, | |
296 | { | |
297 | "description": "null is null", | |
298 | "data": null, | |
299 | "valid": true | |
300 | } | |
301 | ] | |
302 | }, | |
303 | { | |
304 | "description": "multiple types can be specified in an array", | |
305 | "schema": {"type": ["integer", "string"]}, | |
306 | "tests": [ | |
307 | { | |
308 | "description": "an integer is valid", | |
309 | "data": 1, | |
310 | "valid": true | |
311 | }, | |
312 | { | |
313 | "description": "a string is valid", | |
314 | "data": "foo", | |
315 | "valid": true | |
316 | }, | |
317 | { | |
318 | "description": "a float is invalid", | |
319 | "data": 1.1, | |
320 | "valid": false | |
321 | }, | |
322 | { | |
323 | "description": "an object is invalid", | |
324 | "data": {}, | |
325 | "valid": false | |
326 | }, | |
327 | { | |
328 | "description": "an array is invalid", | |
329 | "data": [], | |
330 | "valid": false | |
331 | }, | |
332 | { | |
333 | "description": "a boolean is invalid", | |
334 | "data": true, | |
335 | "valid": false | |
336 | }, | |
337 | { | |
338 | "description": "null is invalid", | |
339 | "data": null, | |
340 | "valid": false | |
341 | } | |
342 | ] | |
343 | } | |
344 | ] |
0 | [ | |
1 | { | |
2 | "description": "uniqueItems validation", | |
3 | "schema": {"uniqueItems": true}, | |
4 | "tests": [ | |
5 | { | |
6 | "description": "unique array of integers is valid", | |
7 | "data": [1, 2], | |
8 | "valid": true | |
9 | }, | |
10 | { | |
11 | "description": "non-unique array of integers is invalid", | |
12 | "data": [1, 1], | |
13 | "valid": false | |
14 | }, | |
15 | { | |
16 | "description": "numbers are unique if mathematically unequal", | |
17 | "data": [1.0, 1.00, 1], | |
18 | "valid": false | |
19 | }, | |
20 | { | |
21 | "description": "unique array of objects is valid", | |
22 | "data": [{"foo": "bar"}, {"foo": "baz"}], | |
23 | "valid": true | |
24 | }, | |
25 | { | |
26 | "description": "non-unique array of objects is invalid", | |
27 | "data": [{"foo": "bar"}, {"foo": "bar"}], | |
28 | "valid": false | |
29 | }, | |
30 | { | |
31 | "description": "unique array of nested objects is valid", | |
32 | "data": [ | |
33 | {"foo": {"bar" : {"baz" : true}}}, | |
34 | {"foo": {"bar" : {"baz" : false}}} | |
35 | ], | |
36 | "valid": true | |
37 | }, | |
38 | { | |
39 | "description": "non-unique array of nested objects is invalid", | |
40 | "data": [ | |
41 | {"foo": {"bar" : {"baz" : true}}}, | |
42 | {"foo": {"bar" : {"baz" : true}}} | |
43 | ], | |
44 | "valid": false | |
45 | }, | |
46 | { | |
47 | "description": "unique array of arrays is valid", | |
48 | "data": [["foo"], ["bar"]], | |
49 | "valid": true | |
50 | }, | |
51 | { | |
52 | "description": "non-unique array of arrays is invalid", | |
53 | "data": [["foo"], ["foo"]], | |
54 | "valid": false | |
55 | }, | |
56 | { | |
57 | "description": "1 and true are unique", | |
58 | "data": [1, true], | |
59 | "valid": true | |
60 | }, | |
61 | { | |
62 | "description": "0 and false are unique", | |
63 | "data": [0, false], | |
64 | "valid": true | |
65 | }, | |
66 | { | |
67 | "description": "unique heterogeneous types are valid", | |
68 | "data": [{}, [1], true, null, 1], | |
69 | "valid": true | |
70 | }, | |
71 | { | |
72 | "description": "non-unique heterogeneous types are invalid", | |
73 | "data": [{}, [1], true, null, {}, 1], | |
74 | "valid": false | |
75 | } | |
76 | ] | |
77 | } | |
78 | ] |
0 | {"foo": true} |
0 | {"type":"object","additionalProperties":false,"definitions":{"x":{"type":"integer"}}} |
0 | { | |
1 | "definitions": { | |
2 | "orNull": { | |
3 | "anyOf": [ | |
4 | {"type": "null"}, | |
5 | {"$ref": "#"} | |
6 | ] | |
7 | } | |
8 | }, | |
9 | "type": "string" | |
10 | } |
24 | 24 | |
25 | 25 | package gojsonschema |
26 | 26 | |
27 | // Type constants | |
27 | 28 | const ( |
28 | 29 | TYPE_ARRAY = `array` |
29 | 30 | TYPE_BOOLEAN = `boolean` |
34 | 35 | TYPE_STRING = `string` |
35 | 36 | ) |
36 | 37 | |
38 | // JSON_TYPES hosts the list of type that are supported in JSON | |
37 | 39 | var JSON_TYPES []string |
40 | ||
41 | // SCHEMA_TYPES hosts the list of type that are supported in schemas | |
38 | 42 | var SCHEMA_TYPES []string |
39 | 43 | |
40 | 44 | func init() { |
26 | 26 | |
27 | 27 | import ( |
28 | 28 | "encoding/json" |
29 | "fmt" | |
30 | "math" | |
29 | "math/big" | |
31 | 30 | "reflect" |
32 | "strconv" | |
33 | 31 | ) |
34 | 32 | |
35 | func isKind(what interface{}, kind reflect.Kind) bool { | |
33 | func isKind(what interface{}, kinds ...reflect.Kind) bool { | |
36 | 34 | target := what |
37 | if isJsonNumber(what) { | |
35 | if isJSONNumber(what) { | |
38 | 36 | // JSON Numbers are strings! |
39 | 37 | target = *mustBeNumber(what) |
40 | 38 | } |
41 | return reflect.ValueOf(target).Kind() == kind | |
39 | targetKind := reflect.ValueOf(target).Kind() | |
40 | for _, kind := range kinds { | |
41 | if targetKind == kind { | |
42 | return true | |
43 | } | |
44 | } | |
45 | return false | |
42 | 46 | } |
43 | 47 | |
44 | 48 | func existsMapKey(m map[string]interface{}, k string) bool { |
55 | 59 | return false |
56 | 60 | } |
57 | 61 | |
58 | func marshalToJsonString(value interface{}) (*string, error) { | |
62 | // indexStringInSlice returns the index of the first instance of 'what' in s or -1 if it is not found in s. | |
63 | func indexStringInSlice(s []string, what string) int { | |
64 | for i := range s { | |
65 | if s[i] == what { | |
66 | return i | |
67 | } | |
68 | } | |
69 | return -1 | |
70 | } | |
71 | ||
72 | func marshalToJSONString(value interface{}) (*string, error) { | |
59 | 73 | |
60 | 74 | mBytes, err := json.Marshal(value) |
61 | 75 | if err != nil { |
66 | 80 | return &sBytes, nil |
67 | 81 | } |
68 | 82 | |
69 | func isJsonNumber(what interface{}) bool { | |
83 | func marshalWithoutNumber(value interface{}) (*string, error) { | |
84 | ||
85 | // The JSON is decoded using https://golang.org/pkg/encoding/json/#Decoder.UseNumber | |
86 | // This means the numbers are internally still represented as strings and therefore 1.00 is unequal to 1 | |
87 | // One way to eliminate these differences is to decode and encode the JSON one more time without Decoder.UseNumber | |
88 | // so that these differences in representation are removed | |
89 | ||
90 | jsonString, err := marshalToJSONString(value) | |
91 | if err != nil { | |
92 | return nil, err | |
93 | } | |
94 | ||
95 | var document interface{} | |
96 | ||
97 | err = json.Unmarshal([]byte(*jsonString), &document) | |
98 | if err != nil { | |
99 | return nil, err | |
100 | } | |
101 | ||
102 | return marshalToJSONString(document) | |
103 | } | |
104 | ||
105 | func isJSONNumber(what interface{}) bool { | |
70 | 106 | |
71 | 107 | switch what.(type) { |
72 | 108 | |
77 | 113 | return false |
78 | 114 | } |
79 | 115 | |
80 | func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool, isValidInt32 bool) { | |
116 | func checkJSONInteger(what interface{}) (isInt bool) { | |
81 | 117 | |
82 | 118 | jsonNumber := what.(json.Number) |
83 | 119 | |
84 | f64, errFloat64 := jsonNumber.Float64() | |
85 | s64 := strconv.FormatFloat(f64, 'f', -1, 64) | |
86 | _, errInt64 := strconv.ParseInt(s64, 10, 64) | |
120 | bigFloat, isValidNumber := new(big.Rat).SetString(string(jsonNumber)) | |
87 | 121 | |
88 | isValidFloat64 = errFloat64 == nil | |
89 | isValidInt64 = errInt64 == nil | |
90 | ||
91 | _, errInt32 := strconv.ParseInt(s64, 10, 32) | |
92 | isValidInt32 = isValidInt64 && errInt32 == nil | |
93 | ||
94 | return | |
122 | return isValidNumber && bigFloat.IsInt() | |
95 | 123 | |
96 | 124 | } |
97 | 125 | |
98 | 126 | // same as ECMA Number.MAX_SAFE_INTEGER and Number.MIN_SAFE_INTEGER |
99 | 127 | const ( |
100 | max_json_float = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 | |
101 | min_json_float = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 | |
128 | maxJSONFloat = float64(1<<53 - 1) // 9007199254740991.0 2^53 - 1 | |
129 | minJSONFloat = -float64(1<<53 - 1) //-9007199254740991.0 -2^53 - 1 | |
102 | 130 | ) |
103 | ||
104 | func isFloat64AnInteger(f float64) bool { | |
105 | ||
106 | if math.IsNaN(f) || math.IsInf(f, 0) || f < min_json_float || f > max_json_float { | |
107 | return false | |
108 | } | |
109 | ||
110 | return f == float64(int64(f)) || f == float64(uint64(f)) | |
111 | } | |
112 | 131 | |
113 | 132 | func mustBeInteger(what interface{}) *int { |
114 | 133 | |
115 | if isJsonNumber(what) { | |
134 | if isJSONNumber(what) { | |
116 | 135 | |
117 | 136 | number := what.(json.Number) |
118 | 137 | |
119 | _, _, isValidInt32 := checkJsonNumber(number) | |
138 | isInt := checkJSONInteger(number) | |
120 | 139 | |
121 | if isValidInt32 { | |
140 | if isInt { | |
122 | 141 | |
123 | 142 | int64Value, err := number.Int64() |
124 | 143 | if err != nil { |
127 | 146 | |
128 | 147 | int32Value := int(int64Value) |
129 | 148 | return &int32Value |
130 | ||
131 | } else { | |
132 | return nil | |
133 | 149 | } |
134 | 150 | |
135 | 151 | } |
137 | 153 | return nil |
138 | 154 | } |
139 | 155 | |
140 | func mustBeNumber(what interface{}) *float64 { | |
156 | func mustBeNumber(what interface{}) *big.Rat { | |
141 | 157 | |
142 | if isJsonNumber(what) { | |
143 | ||
158 | if isJSONNumber(what) { | |
144 | 159 | number := what.(json.Number) |
145 | float64Value, err := number.Float64() | |
146 | ||
147 | if err == nil { | |
148 | return &float64Value | |
149 | } else { | |
150 | return nil | |
160 | float64Value, success := new(big.Rat).SetString(string(number)) | |
161 | if success { | |
162 | return float64Value | |
151 | 163 | } |
152 | ||
153 | 164 | } |
154 | 165 | |
155 | 166 | return nil |
156 | 167 | |
157 | } | |
158 | ||
159 | // formats a number so that it is displayed as the smallest string possible | |
160 | func resultErrorFormatJsonNumber(n json.Number) string { | |
161 | ||
162 | if int64Value, err := n.Int64(); err == nil { | |
163 | return fmt.Sprintf("%d", int64Value) | |
164 | } | |
165 | ||
166 | float64Value, _ := n.Float64() | |
167 | ||
168 | return fmt.Sprintf("%g", float64Value) | |
169 | } | |
170 | ||
171 | // formats a number so that it is displayed as the smallest string possible | |
172 | func resultErrorFormatNumber(n float64) string { | |
173 | ||
174 | if isFloat64AnInteger(n) { | |
175 | return fmt.Sprintf("%d", int64(n)) | |
176 | } | |
177 | ||
178 | return fmt.Sprintf("%g", n) | |
179 | 168 | } |
180 | 169 | |
181 | 170 | func convertDocumentNode(val interface{}) interface{} { |
25 | 25 | package gojsonschema |
26 | 26 | |
27 | 27 | import ( |
28 | "encoding/json" | |
29 | "testing" | |
30 | ||
28 | 31 | "github.com/stretchr/testify/assert" |
29 | "math" | |
30 | "testing" | |
31 | 32 | ) |
32 | 33 | |
33 | func TestResultErrorFormatNumber(t *testing.T) { | |
34 | func TestCheckJsonNumber(t *testing.T) { | |
35 | var testCases = []struct { | |
36 | isInt bool | |
37 | value json.Number | |
38 | }{ | |
39 | {true, "0"}, | |
40 | {true, "2147483647"}, | |
41 | {true, "-2147483648"}, | |
42 | {true, "9223372036854775807"}, | |
43 | {true, "-9223372036854775808"}, | |
44 | {true, "1.0e+2"}, | |
45 | {true, "1.0e+10"}, | |
46 | {true, "-1.0e+2"}, | |
47 | {true, "-1.0e+10"}, | |
48 | {false, "1.0e-2"}, | |
49 | {false, "number"}, | |
50 | {false, "123number"}, | |
51 | } | |
34 | 52 | |
35 | assert.Equal(t, "1", resultErrorFormatNumber(1)) | |
36 | assert.Equal(t, "-1", resultErrorFormatNumber(-1)) | |
37 | assert.Equal(t, "0", resultErrorFormatNumber(0)) | |
38 | // unfortunately, can not be recognized as float | |
39 | assert.Equal(t, "0", resultErrorFormatNumber(0.0)) | |
40 | ||
41 | assert.Equal(t, "1.001", resultErrorFormatNumber(1.001)) | |
42 | assert.Equal(t, "-1.001", resultErrorFormatNumber(-1.001)) | |
43 | assert.Equal(t, "0.0001", resultErrorFormatNumber(0.0001)) | |
44 | ||
45 | // casting math.MaxInt64 (1<<63 -1) to float back to int64 | |
46 | // becomes negative. obviousely because of bit missinterpretation. | |
47 | // so simply test a slightly smaller "large" integer here | |
48 | assert.Equal(t, "4.611686018427388e+18", resultErrorFormatNumber(1<<62)) | |
49 | // with negative int64 max works | |
50 | assert.Equal(t, "-9.223372036854776e+18", resultErrorFormatNumber(math.MinInt64)) | |
51 | assert.Equal(t, "-4.611686018427388e+18", resultErrorFormatNumber(-1<<62)) | |
52 | ||
53 | assert.Equal(t, "10000000000", resultErrorFormatNumber(1e10)) | |
54 | assert.Equal(t, "-10000000000", resultErrorFormatNumber(-1e10)) | |
55 | ||
56 | assert.Equal(t, "1.000000000001", resultErrorFormatNumber(1.000000000001)) | |
57 | assert.Equal(t, "-1.000000000001", resultErrorFormatNumber(-1.000000000001)) | |
58 | assert.Equal(t, "1e-10", resultErrorFormatNumber(1e-10)) | |
59 | assert.Equal(t, "-1e-10", resultErrorFormatNumber(-1e-10)) | |
60 | assert.Equal(t, "4.6116860184273876e+07", resultErrorFormatNumber(4.611686018427387904e7)) | |
61 | assert.Equal(t, "-4.6116860184273876e+07", resultErrorFormatNumber(-4.611686018427387904e7)) | |
53 | for _, testCase := range testCases { | |
54 | assert.Equal(t, testCase.isInt, checkJSONInteger(testCase.value)) | |
55 | assert.Equal(t, testCase.isInt, checkJSONInteger(testCase.value)) | |
56 | } | |
62 | 57 | |
63 | 58 | } |
26 | 26 | |
27 | 27 | import ( |
28 | 28 | "encoding/json" |
29 | "math/big" | |
29 | 30 | "reflect" |
30 | 31 | "regexp" |
31 | 32 | "strconv" |
33 | 34 | "unicode/utf8" |
34 | 35 | ) |
35 | 36 | |
37 | // Validate loads and validates a JSON schema | |
36 | 38 | func Validate(ls JSONLoader, ld JSONLoader) (*Result, error) { |
37 | ||
38 | var err error | |
39 | ||
40 | 39 | // load schema |
41 | ||
42 | 40 | schema, err := NewSchema(ls) |
43 | 41 | if err != nil { |
44 | 42 | return nil, err |
45 | 43 | } |
46 | ||
47 | // begine validation | |
48 | ||
49 | 44 | return schema.Validate(ld) |
50 | ||
51 | } | |
52 | ||
45 | } | |
46 | ||
47 | // Validate loads and validates a JSON document | |
53 | 48 | func (v *Schema) Validate(l JSONLoader) (*Result, error) { |
54 | ||
55 | // load document | |
56 | ||
57 | 49 | root, err := l.LoadJSON() |
58 | 50 | if err != nil { |
59 | 51 | return nil, err |
60 | 52 | } |
61 | ||
62 | // begin validation | |
63 | ||
53 | return v.validateDocument(root), nil | |
54 | } | |
55 | ||
56 | func (v *Schema) validateDocument(root interface{}) *Result { | |
64 | 57 | result := &Result{} |
65 | context := newJsonContext(STRING_CONTEXT_ROOT, nil) | |
58 | context := NewJsonContext(STRING_CONTEXT_ROOT, nil) | |
66 | 59 | v.rootSchema.validateRecursive(v.rootSchema, root, result, context) |
67 | ||
68 | return result, nil | |
69 | ||
70 | } | |
71 | ||
72 | func (v *subSchema) subValidateWithContext(document interface{}, context *jsonContext) *Result { | |
60 | return result | |
61 | } | |
62 | ||
63 | func (v *subSchema) subValidateWithContext(document interface{}, context *JsonContext) *Result { | |
73 | 64 | result := &Result{} |
74 | 65 | v.validateRecursive(v, document, result, context) |
75 | 66 | return result |
76 | 67 | } |
77 | 68 | |
78 | 69 | // Walker function to validate the json recursively against the subSchema |
79 | func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) { | |
70 | func (v *subSchema) validateRecursive(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) { | |
80 | 71 | |
81 | 72 | if internalLogEnabled { |
82 | 73 | internalLog("validateRecursive %s", context.String()) |
83 | 74 | internalLog(" %v", currentNode) |
75 | } | |
76 | ||
77 | // Handle true/false schema as early as possible as all other fields will be nil | |
78 | if currentSubSchema.pass != nil { | |
79 | if !*currentSubSchema.pass { | |
80 | result.addInternalError( | |
81 | new(FalseError), | |
82 | context, | |
83 | currentNode, | |
84 | ErrorDetails{}, | |
85 | ) | |
86 | } | |
87 | return | |
84 | 88 | } |
85 | 89 | |
86 | 90 | // Handle referenced schemas, returns directly when a $ref is found |
92 | 96 | // Check for null value |
93 | 97 | if currentNode == nil { |
94 | 98 | if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_NULL) { |
95 | result.addError( | |
99 | result.addInternalError( | |
96 | 100 | new(InvalidTypeError), |
97 | 101 | context, |
98 | 102 | currentNode, |
109 | 113 | |
110 | 114 | } else { // Not a null value |
111 | 115 | |
112 | if isJsonNumber(currentNode) { | |
116 | if isJSONNumber(currentNode) { | |
113 | 117 | |
114 | 118 | value := currentNode.(json.Number) |
115 | 119 | |
116 | _, isValidInt64, _ := checkJsonNumber(value) | |
117 | ||
118 | validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isValidInt64 && currentSubSchema.types.Contains(TYPE_INTEGER)) | |
120 | isInt := checkJSONInteger(value) | |
121 | ||
122 | validType := currentSubSchema.types.Contains(TYPE_NUMBER) || (isInt && currentSubSchema.types.Contains(TYPE_INTEGER)) | |
119 | 123 | |
120 | 124 | if currentSubSchema.types.IsTyped() && !validType { |
121 | 125 | |
122 | 126 | givenType := TYPE_INTEGER |
123 | if !isValidInt64 { | |
127 | if !isInt { | |
124 | 128 | givenType = TYPE_NUMBER |
125 | 129 | } |
126 | 130 | |
127 | result.addError( | |
131 | result.addInternalError( | |
128 | 132 | new(InvalidTypeError), |
129 | 133 | context, |
130 | 134 | currentNode, |
153 | 157 | case reflect.Slice: |
154 | 158 | |
155 | 159 | if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_ARRAY) { |
156 | result.addError( | |
160 | result.addInternalError( | |
157 | 161 | new(InvalidTypeError), |
158 | 162 | context, |
159 | 163 | currentNode, |
176 | 180 | |
177 | 181 | case reflect.Map: |
178 | 182 | if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_OBJECT) { |
179 | result.addError( | |
183 | result.addInternalError( | |
180 | 184 | new(InvalidTypeError), |
181 | 185 | context, |
182 | 186 | currentNode, |
201 | 205 | for _, pSchema := range currentSubSchema.propertiesChildren { |
202 | 206 | nextNode, ok := castCurrentNode[pSchema.property] |
203 | 207 | if ok { |
204 | subContext := newJsonContext(pSchema.property, context) | |
208 | subContext := NewJsonContext(pSchema.property, context) | |
205 | 209 | v.validateRecursive(pSchema, nextNode, result, subContext) |
206 | 210 | } |
207 | 211 | } |
211 | 215 | case reflect.Bool: |
212 | 216 | |
213 | 217 | if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_BOOLEAN) { |
214 | result.addError( | |
218 | result.addInternalError( | |
215 | 219 | new(InvalidTypeError), |
216 | 220 | context, |
217 | 221 | currentNode, |
233 | 237 | case reflect.String: |
234 | 238 | |
235 | 239 | if currentSubSchema.types.IsTyped() && !currentSubSchema.types.Contains(TYPE_STRING) { |
236 | result.addError( | |
240 | result.addInternalError( | |
237 | 241 | new(InvalidTypeError), |
238 | 242 | context, |
239 | 243 | currentNode, |
262 | 266 | } |
263 | 267 | |
264 | 268 | // Different kinds of validation there, subSchema / common / array / object / string... |
265 | func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *jsonContext) { | |
269 | func (v *subSchema) validateSchema(currentSubSchema *subSchema, currentNode interface{}, result *Result, context *JsonContext) { | |
266 | 270 | |
267 | 271 | if internalLogEnabled { |
268 | 272 | internalLog("validateSchema %s", context.String()) |
286 | 290 | } |
287 | 291 | if !validatedAnyOf { |
288 | 292 | |
289 | result.addError(new(NumberAnyOfError), context, currentNode, ErrorDetails{}) | |
293 | result.addInternalError(new(NumberAnyOfError), context, currentNode, ErrorDetails{}) | |
290 | 294 | |
291 | 295 | if bestValidationResult != nil { |
292 | 296 | // add error messages of closest matching subSchema as |
312 | 316 | |
313 | 317 | if nbValidated != 1 { |
314 | 318 | |
315 | result.addError(new(NumberOneOfError), context, currentNode, ErrorDetails{}) | |
319 | result.addInternalError(new(NumberOneOfError), context, currentNode, ErrorDetails{}) | |
316 | 320 | |
317 | 321 | if nbValidated == 0 { |
318 | 322 | // add error messages of closest matching subSchema as |
335 | 339 | } |
336 | 340 | |
337 | 341 | if nbValidated != len(currentSubSchema.allOf) { |
338 | result.addError(new(NumberAllOfError), context, currentNode, ErrorDetails{}) | |
342 | result.addInternalError(new(NumberAllOfError), context, currentNode, ErrorDetails{}) | |
339 | 343 | } |
340 | 344 | } |
341 | 345 | |
342 | 346 | if currentSubSchema.not != nil { |
343 | 347 | validationResult := currentSubSchema.not.subValidateWithContext(currentNode, context) |
344 | 348 | if validationResult.Valid() { |
345 | result.addError(new(NumberNotError), context, currentNode, ErrorDetails{}) | |
349 | result.addInternalError(new(NumberNotError), context, currentNode, ErrorDetails{}) | |
346 | 350 | } |
347 | 351 | } |
348 | 352 | |
355 | 359 | case []string: |
356 | 360 | for _, dependOnKey := range dependency { |
357 | 361 | if _, dependencyResolved := currentNode.(map[string]interface{})[dependOnKey]; !dependencyResolved { |
358 | result.addError( | |
362 | result.addInternalError( | |
359 | 363 | new(MissingDependencyError), |
360 | 364 | context, |
361 | 365 | currentNode, |
366 | 370 | |
367 | 371 | case *subSchema: |
368 | 372 | dependency.validateRecursive(dependency, currentNode, result, context) |
369 | ||
370 | 373 | } |
371 | 374 | } |
372 | 375 | } |
373 | 376 | } |
374 | 377 | } |
375 | 378 | |
379 | if currentSubSchema._if != nil { | |
380 | validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context) | |
381 | if currentSubSchema._then != nil && validationResultIf.Valid() { | |
382 | validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context) | |
383 | if !validationResultThen.Valid() { | |
384 | result.addInternalError(new(ConditionThenError), context, currentNode, ErrorDetails{}) | |
385 | result.mergeErrors(validationResultThen) | |
386 | } | |
387 | } | |
388 | if currentSubSchema._else != nil && !validationResultIf.Valid() { | |
389 | validationResultElse := currentSubSchema._else.subValidateWithContext(currentNode, context) | |
390 | if !validationResultElse.Valid() { | |
391 | result.addInternalError(new(ConditionElseError), context, currentNode, ErrorDetails{}) | |
392 | result.mergeErrors(validationResultElse) | |
393 | } | |
394 | } | |
395 | } | |
396 | ||
376 | 397 | result.incrementScore() |
377 | 398 | } |
378 | 399 | |
379 | func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { | |
400 | func (v *subSchema) validateCommon(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { | |
380 | 401 | |
381 | 402 | if internalLogEnabled { |
382 | 403 | internalLog("validateCommon %s", context.String()) |
383 | 404 | internalLog(" %v", value) |
384 | 405 | } |
385 | 406 | |
407 | // const: | |
408 | if currentSubSchema._const != nil { | |
409 | vString, err := marshalWithoutNumber(value) | |
410 | if err != nil { | |
411 | result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) | |
412 | } | |
413 | if *vString != *currentSubSchema._const { | |
414 | result.addInternalError(new(ConstError), | |
415 | context, | |
416 | value, | |
417 | ErrorDetails{ | |
418 | "allowed": *currentSubSchema._const, | |
419 | }, | |
420 | ) | |
421 | } | |
422 | } | |
423 | ||
386 | 424 | // enum: |
387 | 425 | if len(currentSubSchema.enum) > 0 { |
388 | has, err := currentSubSchema.ContainsEnum(value) | |
426 | vString, err := marshalWithoutNumber(value) | |
389 | 427 | if err != nil { |
390 | result.addError(new(InternalError), context, value, ErrorDetails{"error": err}) | |
391 | } | |
392 | if !has { | |
393 | result.addError( | |
428 | result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) | |
429 | } | |
430 | if !isStringInSlice(currentSubSchema.enum, *vString) { | |
431 | result.addInternalError( | |
394 | 432 | new(EnumError), |
395 | 433 | context, |
396 | 434 | value, |
404 | 442 | result.incrementScore() |
405 | 443 | } |
406 | 444 | |
407 | func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *jsonContext) { | |
445 | func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface{}, result *Result, context *JsonContext) { | |
408 | 446 | |
409 | 447 | if internalLogEnabled { |
410 | 448 | internalLog("validateArray %s", context.String()) |
416 | 454 | // TODO explain |
417 | 455 | if currentSubSchema.itemsChildrenIsSingleSchema { |
418 | 456 | for i := range value { |
419 | subContext := newJsonContext(strconv.Itoa(i), context) | |
457 | subContext := NewJsonContext(strconv.Itoa(i), context) | |
420 | 458 | validationResult := currentSubSchema.itemsChildren[0].subValidateWithContext(value[i], subContext) |
421 | 459 | result.mergeErrors(validationResult) |
422 | 460 | } |
427 | 465 | |
428 | 466 | // while we have both schemas and values, check them against each other |
429 | 467 | for i := 0; i != nbItems && i != nbValues; i++ { |
430 | subContext := newJsonContext(strconv.Itoa(i), context) | |
468 | subContext := NewJsonContext(strconv.Itoa(i), context) | |
431 | 469 | validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext) |
432 | 470 | result.mergeErrors(validationResult) |
433 | 471 | } |
439 | 477 | switch currentSubSchema.additionalItems.(type) { |
440 | 478 | case bool: |
441 | 479 | if !currentSubSchema.additionalItems.(bool) { |
442 | result.addError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{}) | |
480 | result.addInternalError(new(ArrayNoAdditionalItemsError), context, value, ErrorDetails{}) | |
443 | 481 | } |
444 | 482 | case *subSchema: |
445 | 483 | additionalItemSchema := currentSubSchema.additionalItems.(*subSchema) |
446 | 484 | for i := nbItems; i != nbValues; i++ { |
447 | subContext := newJsonContext(strconv.Itoa(i), context) | |
485 | subContext := NewJsonContext(strconv.Itoa(i), context) | |
448 | 486 | validationResult := additionalItemSchema.subValidateWithContext(value[i], subContext) |
449 | 487 | result.mergeErrors(validationResult) |
450 | 488 | } |
456 | 494 | // minItems & maxItems |
457 | 495 | if currentSubSchema.minItems != nil { |
458 | 496 | if nbValues < int(*currentSubSchema.minItems) { |
459 | result.addError( | |
497 | result.addInternalError( | |
460 | 498 | new(ArrayMinItemsError), |
461 | 499 | context, |
462 | 500 | value, |
466 | 504 | } |
467 | 505 | if currentSubSchema.maxItems != nil { |
468 | 506 | if nbValues > int(*currentSubSchema.maxItems) { |
469 | result.addError( | |
507 | result.addInternalError( | |
470 | 508 | new(ArrayMaxItemsError), |
471 | 509 | context, |
472 | 510 | value, |
477 | 515 | |
478 | 516 | // uniqueItems: |
479 | 517 | if currentSubSchema.uniqueItems { |
480 | var stringifiedItems []string | |
481 | for _, v := range value { | |
482 | vString, err := marshalToJsonString(v) | |
518 | var stringifiedItems = make(map[string]int) | |
519 | for j, v := range value { | |
520 | vString, err := marshalWithoutNumber(v) | |
483 | 521 | if err != nil { |
484 | result.addError(new(InternalError), context, value, ErrorDetails{"err": err}) | |
485 | } | |
486 | if isStringInSlice(stringifiedItems, *vString) { | |
487 | result.addError( | |
522 | result.addInternalError(new(InternalError), context, value, ErrorDetails{"err": err}) | |
523 | } | |
524 | if i, ok := stringifiedItems[*vString]; ok { | |
525 | result.addInternalError( | |
488 | 526 | new(ItemsMustBeUniqueError), |
489 | 527 | context, |
490 | 528 | value, |
491 | ErrorDetails{"type": TYPE_ARRAY}, | |
529 | ErrorDetails{"type": TYPE_ARRAY, "i": i, "j": j}, | |
492 | 530 | ) |
493 | 531 | } |
494 | stringifiedItems = append(stringifiedItems, *vString) | |
532 | stringifiedItems[*vString] = j | |
533 | } | |
534 | } | |
535 | ||
536 | // contains: | |
537 | ||
538 | if currentSubSchema.contains != nil { | |
539 | validatedOne := false | |
540 | var bestValidationResult *Result | |
541 | ||
542 | for i, v := range value { | |
543 | subContext := NewJsonContext(strconv.Itoa(i), context) | |
544 | ||
545 | validationResult := currentSubSchema.contains.subValidateWithContext(v, subContext) | |
546 | if validationResult.Valid() { | |
547 | validatedOne = true | |
548 | break | |
549 | } else { | |
550 | if bestValidationResult == nil || validationResult.score > bestValidationResult.score { | |
551 | bestValidationResult = validationResult | |
552 | } | |
553 | } | |
554 | } | |
555 | if !validatedOne { | |
556 | result.addInternalError( | |
557 | new(ArrayContainsError), | |
558 | context, | |
559 | value, | |
560 | ErrorDetails{}, | |
561 | ) | |
562 | if bestValidationResult != nil { | |
563 | result.mergeErrors(bestValidationResult) | |
564 | } | |
495 | 565 | } |
496 | 566 | } |
497 | 567 | |
498 | 568 | result.incrementScore() |
499 | 569 | } |
500 | 570 | |
501 | func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *jsonContext) { | |
571 | func (v *subSchema) validateObject(currentSubSchema *subSchema, value map[string]interface{}, result *Result, context *JsonContext) { | |
502 | 572 | |
503 | 573 | if internalLogEnabled { |
504 | 574 | internalLog("validateObject %s", context.String()) |
508 | 578 | // minProperties & maxProperties: |
509 | 579 | if currentSubSchema.minProperties != nil { |
510 | 580 | if len(value) < int(*currentSubSchema.minProperties) { |
511 | result.addError( | |
581 | result.addInternalError( | |
512 | 582 | new(ArrayMinPropertiesError), |
513 | 583 | context, |
514 | 584 | value, |
518 | 588 | } |
519 | 589 | if currentSubSchema.maxProperties != nil { |
520 | 590 | if len(value) > int(*currentSubSchema.maxProperties) { |
521 | result.addError( | |
591 | result.addInternalError( | |
522 | 592 | new(ArrayMaxPropertiesError), |
523 | 593 | context, |
524 | 594 | value, |
533 | 603 | if ok { |
534 | 604 | result.incrementScore() |
535 | 605 | } else { |
536 | result.addError( | |
606 | result.addInternalError( | |
537 | 607 | new(RequiredError), |
538 | 608 | context, |
539 | 609 | value, |
543 | 613 | } |
544 | 614 | |
545 | 615 | // additionalProperty & patternProperty: |
546 | if currentSubSchema.additionalProperties != nil { | |
547 | ||
548 | switch currentSubSchema.additionalProperties.(type) { | |
549 | case bool: | |
550 | ||
551 | if !currentSubSchema.additionalProperties.(bool) { | |
552 | ||
553 | for pk := range value { | |
554 | ||
555 | found := false | |
556 | for _, spValue := range currentSubSchema.propertiesChildren { | |
557 | if pk == spValue.property { | |
558 | found = true | |
559 | } | |
560 | } | |
561 | ||
562 | pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) | |
563 | ||
564 | if found { | |
565 | ||
566 | if pp_has && !pp_match { | |
567 | result.addError( | |
568 | new(AdditionalPropertyNotAllowedError), | |
569 | context, | |
570 | value, | |
571 | ErrorDetails{"property": pk}, | |
572 | ) | |
573 | } | |
574 | ||
575 | } else { | |
576 | ||
577 | if !pp_has || !pp_match { | |
578 | result.addError( | |
579 | new(AdditionalPropertyNotAllowedError), | |
580 | context, | |
581 | value, | |
582 | ErrorDetails{"property": pk}, | |
583 | ) | |
584 | } | |
585 | ||
586 | } | |
587 | } | |
588 | } | |
589 | ||
590 | case *subSchema: | |
591 | ||
592 | additionalPropertiesSchema := currentSubSchema.additionalProperties.(*subSchema) | |
593 | for pk := range value { | |
594 | ||
595 | found := false | |
596 | for _, spValue := range currentSubSchema.propertiesChildren { | |
597 | if pk == spValue.property { | |
598 | found = true | |
599 | } | |
600 | } | |
601 | ||
602 | pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) | |
603 | ||
604 | if found { | |
605 | ||
606 | if pp_has && !pp_match { | |
607 | validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context) | |
608 | result.mergeErrors(validationResult) | |
609 | } | |
610 | ||
611 | } else { | |
612 | ||
613 | if !pp_has || !pp_match { | |
614 | validationResult := additionalPropertiesSchema.subValidateWithContext(value[pk], context) | |
615 | result.mergeErrors(validationResult) | |
616 | } | |
617 | ||
618 | } | |
619 | ||
620 | } | |
621 | } | |
622 | } else { | |
623 | ||
616 | for pk := range value { | |
617 | ||
618 | // Check whether this property is described by "properties" | |
619 | found := false | |
620 | for _, spValue := range currentSubSchema.propertiesChildren { | |
621 | if pk == spValue.property { | |
622 | found = true | |
623 | } | |
624 | } | |
625 | ||
626 | // Check whether this property is described by "patternProperties" | |
627 | ppMatch := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) | |
628 | ||
629 | // If it is not described by neither "properties" nor "patternProperties" it must pass "additionalProperties" | |
630 | if !found && !ppMatch { | |
631 | switch ap := currentSubSchema.additionalProperties.(type) { | |
632 | case bool: | |
633 | // Handle the boolean case separately as it's cleaner to return a specific error than failing to pass the false schema | |
634 | if !ap { | |
635 | result.addInternalError( | |
636 | new(AdditionalPropertyNotAllowedError), | |
637 | context, | |
638 | value[pk], | |
639 | ErrorDetails{"property": pk}, | |
640 | ) | |
641 | ||
642 | } | |
643 | case *subSchema: | |
644 | validationResult := ap.subValidateWithContext(value[pk], NewJsonContext(pk, context)) | |
645 | result.mergeErrors(validationResult) | |
646 | } | |
647 | } | |
648 | } | |
649 | ||
650 | // propertyNames: | |
651 | if currentSubSchema.propertyNames != nil { | |
624 | 652 | for pk := range value { |
625 | ||
626 | pp_has, pp_match := v.validatePatternProperty(currentSubSchema, pk, value[pk], result, context) | |
627 | ||
628 | if pp_has && !pp_match { | |
629 | ||
630 | result.addError( | |
631 | new(InvalidPropertyPatternError), | |
653 | validationResult := currentSubSchema.propertyNames.subValidateWithContext(pk, context) | |
654 | if !validationResult.Valid() { | |
655 | result.addInternalError(new(InvalidPropertyNameError), | |
632 | 656 | context, |
633 | value, | |
634 | ErrorDetails{ | |
657 | value, ErrorDetails{ | |
635 | 658 | "property": pk, |
636 | "pattern": currentSubSchema.PatternPropertiesString(), | |
637 | }, | |
638 | ) | |
639 | } | |
640 | ||
659 | }) | |
660 | result.mergeErrors(validationResult) | |
661 | } | |
641 | 662 | } |
642 | 663 | } |
643 | 664 | |
644 | 665 | result.incrementScore() |
645 | 666 | } |
646 | 667 | |
647 | func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *jsonContext) (has bool, matched bool) { | |
668 | func (v *subSchema) validatePatternProperty(currentSubSchema *subSchema, key string, value interface{}, result *Result, context *JsonContext) bool { | |
648 | 669 | |
649 | 670 | if internalLogEnabled { |
650 | 671 | internalLog("validatePatternProperty %s", context.String()) |
651 | 672 | internalLog(" %s %v", key, value) |
652 | 673 | } |
653 | 674 | |
654 | has = false | |
655 | ||
656 | validatedkey := false | |
675 | validated := false | |
657 | 676 | |
658 | 677 | for pk, pv := range currentSubSchema.patternProperties { |
659 | 678 | if matches, _ := regexp.MatchString(pk, key); matches { |
660 | has = true | |
661 | subContext := newJsonContext(key, context) | |
679 | validated = true | |
680 | subContext := NewJsonContext(key, context) | |
662 | 681 | validationResult := pv.subValidateWithContext(value, subContext) |
663 | 682 | result.mergeErrors(validationResult) |
664 | if validationResult.Valid() { | |
665 | validatedkey = true | |
666 | } | |
667 | } | |
668 | } | |
669 | ||
670 | if !validatedkey { | |
671 | return has, false | |
683 | } | |
684 | } | |
685 | ||
686 | if !validated { | |
687 | return false | |
672 | 688 | } |
673 | 689 | |
674 | 690 | result.incrementScore() |
675 | ||
676 | return has, true | |
677 | } | |
678 | ||
679 | func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { | |
691 | return true | |
692 | } | |
693 | ||
694 | func (v *subSchema) validateString(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { | |
680 | 695 | |
681 | 696 | // Ignore JSON numbers |
682 | if isJsonNumber(value) { | |
697 | if isJSONNumber(value) { | |
683 | 698 | return |
684 | 699 | } |
685 | 700 | |
698 | 713 | // minLength & maxLength: |
699 | 714 | if currentSubSchema.minLength != nil { |
700 | 715 | if utf8.RuneCount([]byte(stringValue)) < int(*currentSubSchema.minLength) { |
701 | result.addError( | |
716 | result.addInternalError( | |
702 | 717 | new(StringLengthGTEError), |
703 | 718 | context, |
704 | 719 | value, |
708 | 723 | } |
709 | 724 | if currentSubSchema.maxLength != nil { |
710 | 725 | if utf8.RuneCount([]byte(stringValue)) > int(*currentSubSchema.maxLength) { |
711 | result.addError( | |
726 | result.addInternalError( | |
712 | 727 | new(StringLengthLTEError), |
713 | 728 | context, |
714 | 729 | value, |
720 | 735 | // pattern: |
721 | 736 | if currentSubSchema.pattern != nil { |
722 | 737 | if !currentSubSchema.pattern.MatchString(stringValue) { |
723 | result.addError( | |
738 | result.addInternalError( | |
724 | 739 | new(DoesNotMatchPatternError), |
725 | 740 | context, |
726 | 741 | value, |
733 | 748 | // format |
734 | 749 | if currentSubSchema.format != "" { |
735 | 750 | if !FormatCheckers.IsFormat(currentSubSchema.format, stringValue) { |
736 | result.addError( | |
751 | result.addInternalError( | |
737 | 752 | new(DoesNotMatchFormatError), |
738 | 753 | context, |
739 | 754 | value, |
745 | 760 | result.incrementScore() |
746 | 761 | } |
747 | 762 | |
748 | func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *jsonContext) { | |
763 | func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{}, result *Result, context *JsonContext) { | |
749 | 764 | |
750 | 765 | // Ignore non numbers |
751 | if !isJsonNumber(value) { | |
766 | if !isJSONNumber(value) { | |
752 | 767 | return |
753 | 768 | } |
754 | 769 | |
758 | 773 | } |
759 | 774 | |
760 | 775 | number := value.(json.Number) |
761 | float64Value, _ := number.Float64() | |
776 | float64Value, _ := new(big.Rat).SetString(string(number)) | |
762 | 777 | |
763 | 778 | // multipleOf: |
764 | 779 | if currentSubSchema.multipleOf != nil { |
765 | ||
766 | if !isFloat64AnInteger(float64Value / *currentSubSchema.multipleOf) { | |
767 | result.addError( | |
780 | if q := new(big.Rat).Quo(float64Value, currentSubSchema.multipleOf); !q.IsInt() { | |
781 | result.addInternalError( | |
768 | 782 | new(MultipleOfError), |
769 | 783 | context, |
770 | resultErrorFormatJsonNumber(number), | |
771 | ErrorDetails{"multiple": *currentSubSchema.multipleOf}, | |
784 | number, | |
785 | ErrorDetails{ | |
786 | "multiple": new(big.Float).SetRat(currentSubSchema.multipleOf), | |
787 | }, | |
772 | 788 | ) |
773 | 789 | } |
774 | 790 | } |
775 | 791 | |
776 | 792 | //maximum & exclusiveMaximum: |
777 | 793 | if currentSubSchema.maximum != nil { |
778 | if currentSubSchema.exclusiveMaximum { | |
779 | if float64Value >= *currentSubSchema.maximum { | |
780 | result.addError( | |
781 | new(NumberLTError), | |
782 | context, | |
783 | resultErrorFormatJsonNumber(number), | |
784 | ErrorDetails{ | |
785 | "max": resultErrorFormatNumber(*currentSubSchema.maximum), | |
786 | }, | |
787 | ) | |
788 | } | |
789 | } else { | |
790 | if float64Value > *currentSubSchema.maximum { | |
791 | result.addError( | |
792 | new(NumberLTEError), | |
793 | context, | |
794 | resultErrorFormatJsonNumber(number), | |
795 | ErrorDetails{ | |
796 | "max": resultErrorFormatNumber(*currentSubSchema.maximum), | |
797 | }, | |
798 | ) | |
799 | } | |
794 | if float64Value.Cmp(currentSubSchema.maximum) == 1 { | |
795 | result.addInternalError( | |
796 | new(NumberLTEError), | |
797 | context, | |
798 | number, | |
799 | ErrorDetails{ | |
800 | "max": new(big.Float).SetRat(currentSubSchema.maximum), | |
801 | }, | |
802 | ) | |
803 | } | |
804 | } | |
805 | if currentSubSchema.exclusiveMaximum != nil { | |
806 | if float64Value.Cmp(currentSubSchema.exclusiveMaximum) >= 0 { | |
807 | result.addInternalError( | |
808 | new(NumberLTError), | |
809 | context, | |
810 | number, | |
811 | ErrorDetails{ | |
812 | "max": new(big.Float).SetRat(currentSubSchema.exclusiveMaximum), | |
813 | }, | |
814 | ) | |
800 | 815 | } |
801 | 816 | } |
802 | 817 | |
803 | 818 | //minimum & exclusiveMinimum: |
804 | 819 | if currentSubSchema.minimum != nil { |
805 | if currentSubSchema.exclusiveMinimum { | |
806 | if float64Value <= *currentSubSchema.minimum { | |
807 | result.addError( | |
808 | new(NumberGTError), | |
809 | context, | |
810 | resultErrorFormatJsonNumber(number), | |
811 | ErrorDetails{ | |
812 | "min": resultErrorFormatNumber(*currentSubSchema.minimum), | |
813 | }, | |
814 | ) | |
815 | } | |
816 | } else { | |
817 | if float64Value < *currentSubSchema.minimum { | |
818 | result.addError( | |
819 | new(NumberGTEError), | |
820 | context, | |
821 | resultErrorFormatJsonNumber(number), | |
822 | ErrorDetails{ | |
823 | "min": resultErrorFormatNumber(*currentSubSchema.minimum), | |
824 | }, | |
825 | ) | |
826 | } | |
820 | if float64Value.Cmp(currentSubSchema.minimum) == -1 { | |
821 | result.addInternalError( | |
822 | new(NumberGTEError), | |
823 | context, | |
824 | number, | |
825 | ErrorDetails{ | |
826 | "min": new(big.Float).SetRat(currentSubSchema.minimum), | |
827 | }, | |
828 | ) | |
829 | } | |
830 | } | |
831 | if currentSubSchema.exclusiveMinimum != nil { | |
832 | if float64Value.Cmp(currentSubSchema.exclusiveMinimum) <= 0 { | |
833 | result.addInternalError( | |
834 | new(NumberGTError), | |
835 | context, | |
836 | number, | |
837 | ErrorDetails{ | |
838 | "min": new(big.Float).SetRat(currentSubSchema.exclusiveMinimum), | |
839 | }, | |
840 | ) | |
841 | } | |
842 | } | |
843 | ||
844 | // format | |
845 | if currentSubSchema.format != "" { | |
846 | if !FormatCheckers.IsFormat(currentSubSchema.format, float64Value) { | |
847 | result.addInternalError( | |
848 | new(DoesNotMatchFormatError), | |
849 | context, | |
850 | value, | |
851 | ErrorDetails{"format": currentSubSchema.format}, | |
852 | ) | |
827 | 853 | } |
828 | 854 | } |
829 | 855 |