diff --git a/go.mod b/go.mod index 9bf8c151..17bddeb6 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.23.0 require ( github.com/google/go-cmp v0.7.0 + github.com/google/jsonschema-go v0.2.0 github.com/yosida95/uritemplate/v3 v3.0.2 golang.org/x/tools v0.34.0 ) diff --git a/go.sum b/go.sum index 7d2f581d..a2edf9ad 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.2.0 h1:Uh19091iHC56//WOsAd1oRg6yy1P9BpSvpjOL6RcjLQ= +github.com/google/jsonschema-go v0.2.0/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= diff --git a/jsonschema/LICENSE b/jsonschema/LICENSE deleted file mode 100644 index 508be926..00000000 --- a/jsonschema/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 Go MCP SDK Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/jsonschema/README.md b/jsonschema/README.md deleted file mode 100644 index f316bedd..00000000 --- a/jsonschema/README.md +++ /dev/null @@ -1,39 +0,0 @@ -TODO: this file should live at the root of the jsonschema-go module, -above the jsonschema package. - -# JSON Schema for GO - -This module implements the [JSON Schema](https://json-schema.org/) specification. -The `jsonschema` package supports creating schemas, validating JSON values -against a schema, and inferring a schema from a Go struct. See the package -documentation for usage. - -## Contributing - -This module welcomes external contributions. -It has no dependencies outside of the standard library, and can be built with -the standard Go toolchain. Run `go test ./...` at the module root to run all -the tests. - -## Issues - -This project uses the [GitHub issue -tracker](https://github.com/TODO/jsonschema-go/issues) for bug reports, feature requests, and other issues. - -Please [report -bugs](https://github.com/TODO/jsonschema-go/issues/new). If the SDK is -not working as you expected, it is likely due to a bug or inadequate -documentation, and reporting an issue will help us address this shortcoming. - -When reporting a bug, make sure to answer these five questions: - -1. What did you do? -2. What did you see? -3. What did you expect to see? -4. What version of the Go MCP SDK are you using? -5. What version of Go are you using (`go version`)? - -## License - -This project is licensed under the MIT license. See the LICENSE file for details. - diff --git a/jsonschema/annotations.go b/jsonschema/annotations.go deleted file mode 100644 index a7ede1c6..00000000 --- a/jsonschema/annotations.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import "maps" - -// An annotations tracks certain properties computed by keywords that are used by validation. -// ("Annotation" is the spec's term.) -// In particular, the unevaluatedItems and unevaluatedProperties keywords need to know which -// items and properties were evaluated (validated successfully). -type annotations struct { - allItems bool // all items were evaluated - endIndex int // 1+largest index evaluated by prefixItems - evaluatedIndexes map[int]bool // set of indexes evaluated by contains - allProperties bool // all properties were evaluated - evaluatedProperties map[string]bool // set of properties evaluated by various keywords -} - -// noteIndex marks i as evaluated. -func (a *annotations) noteIndex(i int) { - if a.evaluatedIndexes == nil { - a.evaluatedIndexes = map[int]bool{} - } - a.evaluatedIndexes[i] = true -} - -// noteEndIndex marks items with index less than end as evaluated. -func (a *annotations) noteEndIndex(end int) { - if end > a.endIndex { - a.endIndex = end - } -} - -// noteProperty marks prop as evaluated. -func (a *annotations) noteProperty(prop string) { - if a.evaluatedProperties == nil { - a.evaluatedProperties = map[string]bool{} - } - a.evaluatedProperties[prop] = true -} - -// noteProperties marks all the properties in props as evaluated. -func (a *annotations) noteProperties(props map[string]bool) { - a.evaluatedProperties = merge(a.evaluatedProperties, props) -} - -// merge adds b's annotations to a. -// a must not be nil. -func (a *annotations) merge(b *annotations) { - if b == nil { - return - } - if b.allItems { - a.allItems = true - } - if b.endIndex > a.endIndex { - a.endIndex = b.endIndex - } - a.evaluatedIndexes = merge(a.evaluatedIndexes, b.evaluatedIndexes) - if b.allProperties { - a.allProperties = true - } - a.evaluatedProperties = merge(a.evaluatedProperties, b.evaluatedProperties) -} - -// merge adds t's keys to s and returns s. -// If s is nil, it returns a copy of t. -func merge[K comparable](s, t map[K]bool) map[K]bool { - if s == nil { - return maps.Clone(t) - } - maps.Copy(s, t) - return s -} diff --git a/jsonschema/doc.go b/jsonschema/doc.go deleted file mode 100644 index 0f0ba441..00000000 --- a/jsonschema/doc.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -/* -Package jsonschema is an implementation of the [JSON Schema specification], -a JSON-based format for describing the structure of JSON data. -The package can be used to read schemas for code generation, and to validate -data using the draft 2020-12 specification. Validation with other drafts -or custom meta-schemas is not supported. - -Construct a [Schema] as you would any Go struct (for example, by writing -a struct literal), or unmarshal a JSON schema into a [Schema] in the usual -way (with [encoding/json], for instance). It can then be used for code -generation or other purposes without further processing. -You can also infer a schema from a Go struct. - -# Resolution - -A Schema can refer to other schemas, both inside and outside itself. These -references must be resolved before a schema can be used for validation. -Call [Schema.Resolve] to obtain a resolved schema (called a [Resolved]). -If the schema has external references, pass a [ResolveOptions] with a [Loader] -to load them. To validate default values in a schema, set -[ResolveOptions.ValidateDefaults] to true. - -# Validation - -Call [Resolved.Validate] to validate a JSON value. The value must be a -Go value that looks like the result of unmarshaling a JSON value into an -[any] or a struct. For example, the JSON value - - {"name": "Al", "scores": [90, 80, 100]} - -could be represented as the Go value - - map[string]any{ - "name": "Al", - "scores": []any{90, 80, 100}, - } - -or as a value of this type: - - type Player struct { - Name string `json:"name"` - Scores []int `json:"scores"` - } - -# Inference - -The [For] function returns a [Schema] describing the given Go type. -Each field in the struct becomes a property of the schema. -The values of "json" tags are respected: the field's property name is taken -from the tag, and fields omitted from the JSON are omitted from the schema as -well. -For example, `jsonschema.For[Player]()` returns this schema: - - { - "properties": { - "name": { - "type": "string" - }, - "scores": { - "type": "array", - "items": {"type": "integer"} - } - "required": ["name", "scores"], - "additionalProperties": {"not": {}} - } - } - -Use the "jsonschema" struct tag to provide a description for the property: - - type Player struct { - Name string `json:"name" jsonschema:"player name"` - Scores []int `json:"scores" jsonschema:"scores of player's games"` - } - -# Deviations from the specification - -Regular expressions are processed with Go's regexp package, which differs -from ECMA 262, most significantly in not supporting back-references. -See [this table of differences] for more. - -The "format" keyword described in [section 7 of the validation spec] is recorded -in the Schema, but is ignored during validation. -It does not even produce [annotations]. -Use the "pattern" keyword instead: it will work more reliably across JSON Schema -implementations. See [learnjsonschema.com] for more recommendations about "format". - -The content keywords described in [section 8 of the validation spec] -are recorded in the schema, but ignored during validation. - -[JSON Schema specification]: https://json-schema.org -[section 7 of the validation spec]: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.7 -[section 8 of the validation spec]: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.8 -[learnjsonschema.com]: https://www.learnjsonschema.com/2020-12/format-annotation/format/ -[this table of differences]: https://github.com/dlclark/regexp2?tab=readme-ov-file#compare-regexp-and-regexp2 -[annotations]: https://json-schema.org/draft/2020-12/json-schema-core#name-annotations -*/ -package jsonschema diff --git a/jsonschema/infer.go b/jsonschema/infer.go deleted file mode 100644 index aa01fcd2..00000000 --- a/jsonschema/infer.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// This file contains functions that infer a schema from a Go type. - -package jsonschema - -import ( - "fmt" - "log/slog" - "maps" - "math/big" - "reflect" - "regexp" - "time" -) - -// ForOptions are options for the [For] function. -type ForOptions struct { - // If IgnoreInvalidTypes is true, fields that can't be represented as a JSON - // Schema are ignored instead of causing an error. - // This allows callers to adjust the resulting schema using custom knowledge. - // For example, an interface type where all the possible implementations are - // known can be described with "oneof". - IgnoreInvalidTypes bool - - // TypeSchemas maps types to their schemas. - // If [For] encounters a type equal to a type of a key in this map, the - // corresponding value is used as the resulting schema (after cloning to - // ensure uniqueness). - // Types in this map override the default translations, as described - // in [For]'s documentation. - TypeSchemas map[any]*Schema -} - -// For constructs a JSON schema object for the given type argument. -// If non-nil, the provided options configure certain aspects of this contruction, -// described below. - -// It translates Go types into compatible JSON schema types, as follows. -// These defaults can be overridden by [ForOptions.TypeSchemas]. -// -// - Strings have schema type "string". -// - Bools have schema type "boolean". -// - Signed and unsigned integer types have schema type "integer". -// - Floating point types have schema type "number". -// - Slices and arrays have schema type "array", and a corresponding schema -// for items. -// - Maps with string key have schema type "object", and corresponding -// schema for additionalProperties. -// - Structs have schema type "object", and disallow additionalProperties. -// Their properties are derived from exported struct fields, using the -// struct field JSON name. Fields that are marked "omitempty" are -// considered optional; all other fields become required properties. -// - Some types in the standard library that implement json.Marshaler -// translate to schemas that match the values to which they marshal. -// For example, [time.Time] translates to the schema for strings. -// -// For will return an error if there is a cycle in the types. -// -// By default, For returns an error if t contains (possibly recursively) any of the -// following Go types, as they are incompatible with the JSON schema spec. -// If [ForOptions.IgnoreInvalidTypes] is true, then these types are ignored instead. -// - maps with key other than 'string' -// - function types -// - channel types -// - complex numbers -// - unsafe pointers -// -// This function recognizes struct field tags named "jsonschema". -// A jsonschema tag on a field is used as the description for the corresponding property. -// For future compatibility, descriptions must not start with "WORD=", where WORD is a -// sequence of non-whitespace characters. -func For[T any](opts *ForOptions) (*Schema, error) { - if opts == nil { - opts = &ForOptions{} - } - schemas := maps.Clone(initialSchemaMap) - // Add types from the options. They override the default ones. - for v, s := range opts.TypeSchemas { - schemas[reflect.TypeOf(v)] = s - } - s, err := forType(reflect.TypeFor[T](), map[reflect.Type]bool{}, opts.IgnoreInvalidTypes, schemas) - if err != nil { - var z T - return nil, fmt.Errorf("For[%T](): %w", z, err) - } - return s, nil -} - -// ForType is like [For], but takes a [reflect.Type] -func ForType(t reflect.Type, opts *ForOptions) (*Schema, error) { - schemas := maps.Clone(initialSchemaMap) - // Add types from the options. They override the default ones. - for v, s := range opts.TypeSchemas { - schemas[reflect.TypeOf(v)] = s - } - s, err := forType(t, map[reflect.Type]bool{}, opts.IgnoreInvalidTypes, schemas) - if err != nil { - return nil, fmt.Errorf("ForType(%s): %w", t, err) - } - return s, nil -} - -func forType(t reflect.Type, seen map[reflect.Type]bool, ignore bool, schemas map[reflect.Type]*Schema) (*Schema, error) { - // Follow pointers: the schema for *T is almost the same as for T, except that - // an explicit JSON "null" is allowed for the pointer. - allowNull := false - for t.Kind() == reflect.Pointer { - allowNull = true - t = t.Elem() - } - - // Check for cycles - // User defined types have a name, so we can skip those that are natively defined - if t.Name() != "" { - if seen[t] { - return nil, fmt.Errorf("cycle detected for type %v", t) - } - seen[t] = true - defer delete(seen, t) - } - - if s := schemas[t]; s != nil { - return s.CloneSchemas(), nil - } - - var ( - s = new(Schema) - err error - ) - - switch t.Kind() { - case reflect.Bool: - s.Type = "boolean" - - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Uintptr: - s.Type = "integer" - - case reflect.Float32, reflect.Float64: - s.Type = "number" - - case reflect.Interface: - // Unrestricted - - case reflect.Map: - if t.Key().Kind() != reflect.String { - if ignore { - return nil, nil // ignore - } - return nil, fmt.Errorf("unsupported map key type %v", t.Key().Kind()) - } - if t.Key().Kind() != reflect.String { - } - s.Type = "object" - s.AdditionalProperties, err = forType(t.Elem(), seen, ignore, schemas) - if err != nil { - return nil, fmt.Errorf("computing map value schema: %v", err) - } - if ignore && s.AdditionalProperties == nil { - // Ignore if the element type is invalid. - return nil, nil - } - - case reflect.Slice, reflect.Array: - s.Type = "array" - s.Items, err = forType(t.Elem(), seen, ignore, schemas) - if err != nil { - return nil, fmt.Errorf("computing element schema: %v", err) - } - if ignore && s.Items == nil { - // Ignore if the element type is invalid. - return nil, nil - } - if t.Kind() == reflect.Array { - s.MinItems = Ptr(t.Len()) - s.MaxItems = Ptr(t.Len()) - } - - case reflect.String: - s.Type = "string" - - case reflect.Struct: - s.Type = "object" - // no additional properties are allowed - s.AdditionalProperties = falseSchema() - - for i := range t.NumField() { - field := t.Field(i) - info := fieldJSONInfo(field) - if info.omit { - continue - } - if s.Properties == nil { - s.Properties = make(map[string]*Schema) - } - fs, err := forType(field.Type, seen, ignore, schemas) - if err != nil { - return nil, err - } - if ignore && fs == nil { - // Skip fields of invalid type. - continue - } - if tag, ok := field.Tag.Lookup("jsonschema"); ok { - if tag == "" { - return nil, fmt.Errorf("empty jsonschema tag on struct field %s.%s", t, field.Name) - } - if disallowedPrefixRegexp.MatchString(tag) { - return nil, fmt.Errorf("tag must not begin with 'WORD=': %q", tag) - } - fs.Description = tag - } - s.Properties[info.name] = fs - if !info.settings["omitempty"] && !info.settings["omitzero"] { - s.Required = append(s.Required, info.name) - } - } - - default: - if ignore { - // Ignore. - return nil, nil - } - return nil, fmt.Errorf("type %v is unsupported by jsonschema", t) - } - if allowNull && s.Type != "" { - s.Types = []string{"null", s.Type} - s.Type = "" - } - schemas[t] = s - return s, nil -} - -// initialSchemaMap holds types from the standard library that have MarshalJSON methods. -var initialSchemaMap = make(map[reflect.Type]*Schema) - -func init() { - ss := &Schema{Type: "string"} - initialSchemaMap[reflect.TypeFor[time.Time]()] = ss - initialSchemaMap[reflect.TypeFor[slog.Level]()] = ss - initialSchemaMap[reflect.TypeFor[big.Int]()] = &Schema{Types: []string{"null", "string"}} - initialSchemaMap[reflect.TypeFor[big.Rat]()] = ss - initialSchemaMap[reflect.TypeFor[big.Float]()] = ss -} - -// Disallow jsonschema tag values beginning "WORD=", for future expansion. -var disallowedPrefixRegexp = regexp.MustCompile("^[^ \t\n]*=") diff --git a/jsonschema/infer_test.go b/jsonschema/infer_test.go deleted file mode 100644 index 62bfbbbc..00000000 --- a/jsonschema/infer_test.go +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema_test - -import ( - "log/slog" - "math/big" - "reflect" - "strings" - "testing" - "time" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/modelcontextprotocol/go-sdk/jsonschema" -) - -type custom int - -func forType[T any](ignore bool) *jsonschema.Schema { - var s *jsonschema.Schema - var err error - - opts := &jsonschema.ForOptions{ - IgnoreInvalidTypes: ignore, - TypeSchemas: map[any]*jsonschema.Schema{ - custom(0): {Type: "custom"}, - }, - } - s, err = jsonschema.For[T](opts) - if err != nil { - panic(err) - } - return s -} - -func TestFor(t *testing.T) { - type schema = jsonschema.Schema - - type S struct { - B int `jsonschema:"bdesc"` - } - - type test struct { - name string - got *jsonschema.Schema - want *jsonschema.Schema - } - - tests := func(ignore bool) []test { - return []test{ - {"string", forType[string](ignore), &schema{Type: "string"}}, - {"int", forType[int](ignore), &schema{Type: "integer"}}, - {"int16", forType[int16](ignore), &schema{Type: "integer"}}, - {"uint32", forType[int16](ignore), &schema{Type: "integer"}}, - {"float64", forType[float64](ignore), &schema{Type: "number"}}, - {"bool", forType[bool](ignore), &schema{Type: "boolean"}}, - {"time", forType[time.Time](ignore), &schema{Type: "string"}}, - {"level", forType[slog.Level](ignore), &schema{Type: "string"}}, - {"bigint", forType[big.Int](ignore), &schema{Types: []string{"null", "string"}}}, - {"custom", forType[custom](ignore), &schema{Type: "custom"}}, - {"intmap", forType[map[string]int](ignore), &schema{ - Type: "object", - AdditionalProperties: &schema{Type: "integer"}, - }}, - {"anymap", forType[map[string]any](ignore), &schema{ - Type: "object", - AdditionalProperties: &schema{}, - }}, - { - "struct", - forType[struct { - F int `json:"f" jsonschema:"fdesc"` - G []float64 - P *bool `jsonschema:"pdesc"` - Skip string `json:"-"` - NoSkip string `json:",omitempty"` - unexported float64 - unexported2 int `json:"No"` - }](ignore), - &schema{ - Type: "object", - Properties: map[string]*schema{ - "f": {Type: "integer", Description: "fdesc"}, - "G": {Type: "array", Items: &schema{Type: "number"}}, - "P": {Types: []string{"null", "boolean"}, Description: "pdesc"}, - "NoSkip": {Type: "string"}, - }, - Required: []string{"f", "G", "P"}, - AdditionalProperties: falseSchema(), - }, - }, - { - "no sharing", - forType[struct{ X, Y int }](ignore), - &schema{ - Type: "object", - Properties: map[string]*schema{ - "X": {Type: "integer"}, - "Y": {Type: "integer"}, - }, - Required: []string{"X", "Y"}, - AdditionalProperties: falseSchema(), - }, - }, - { - "nested and embedded", - forType[struct { - A S - S - }](ignore), - &schema{ - Type: "object", - Properties: map[string]*schema{ - "A": { - Type: "object", - Properties: map[string]*schema{ - "B": {Type: "integer", Description: "bdesc"}, - }, - Required: []string{"B"}, - AdditionalProperties: falseSchema(), - }, - "S": { - Type: "object", - Properties: map[string]*schema{ - "B": {Type: "integer", Description: "bdesc"}, - }, - Required: []string{"B"}, - AdditionalProperties: falseSchema(), - }, - }, - Required: []string{"A", "S"}, - AdditionalProperties: falseSchema(), - }, - }, - } - } - - run := func(t *testing.T, tt test) { - if diff := cmp.Diff(tt.want, tt.got, cmpopts.IgnoreUnexported(jsonschema.Schema{})); diff != "" { - t.Fatalf("For mismatch (-want +got):\n%s", diff) - } - // These schemas should all resolve. - if _, err := tt.got.Resolve(nil); err != nil { - t.Fatalf("Resolving: %v", err) - } - } - - t.Run("strict", func(t *testing.T) { - for _, test := range tests(false) { - t.Run(test.name, func(t *testing.T) { run(t, test) }) - } - }) - - laxTests := append(tests(true), test{ - "ignore", - forType[struct { - A int - B map[int]int - C func() - }](true), - &schema{ - Type: "object", - Properties: map[string]*schema{ - "A": {Type: "integer"}, - }, - Required: []string{"A"}, - AdditionalProperties: falseSchema(), - }, - }) - t.Run("lax", func(t *testing.T) { - for _, test := range laxTests { - t.Run(test.name, func(t *testing.T) { run(t, test) }) - } - }) -} - -func TestForType(t *testing.T) { - type schema = jsonschema.Schema - - // ForType is virtually identical to For. Just test that options are handled properly. - opts := &jsonschema.ForOptions{ - IgnoreInvalidTypes: true, - TypeSchemas: map[any]*jsonschema.Schema{ - custom(0): {Type: "custom"}, - }, - } - - type S struct { - I int - F func() - C custom - } - got, err := jsonschema.ForType(reflect.TypeOf(S{}), opts) - if err != nil { - t.Fatal(err) - } - want := &schema{ - Type: "object", - Properties: map[string]*schema{ - "I": {Type: "integer"}, - "C": {Type: "custom"}, - }, - Required: []string{"I", "C"}, - AdditionalProperties: falseSchema(), - } - if diff := cmp.Diff(want, got, cmpopts.IgnoreUnexported(schema{})); diff != "" { - t.Fatalf("ForType mismatch (-want +got):\n%s", diff) - } -} - -func forErr[T any]() error { - _, err := jsonschema.For[T](nil) - return err -} - -func TestForErrors(t *testing.T) { - type ( - s1 struct { - Empty int `jsonschema:""` - } - s2 struct { - Bad int `jsonschema:"$foo=1,bar"` - } - ) - - for _, tt := range []struct { - got error - want string - }{ - {forErr[map[int]int](), "unsupported map key type"}, - {forErr[s1](), "empty jsonschema tag"}, - {forErr[s2](), "must not begin with"}, - {forErr[func()](), "unsupported"}, - } { - if tt.got == nil { - t.Errorf("got nil, want error containing %q", tt.want) - } else if !strings.Contains(tt.got.Error(), tt.want) { - t.Errorf("got %q\nwant it to contain %q", tt.got, tt.want) - } - } -} - -func TestForWithMutation(t *testing.T) { - // This test ensures that the cached schema is not mutated when the caller - // mutates the returned schema. - type S struct { - A int - } - type T struct { - A int `json:"A"` - B map[string]int - C []S - D [3]S - E *bool - } - s, err := jsonschema.For[T](nil) - if err != nil { - t.Fatalf("For: %v", err) - } - s.Required[0] = "mutated" - s.Properties["A"].Type = "mutated" - s.Properties["C"].Items.Type = "mutated" - s.Properties["D"].MaxItems = jsonschema.Ptr(10) - s.Properties["D"].MinItems = jsonschema.Ptr(10) - s.Properties["E"].Types[0] = "mutated" - - s2, err := jsonschema.For[T](nil) - if err != nil { - t.Fatalf("For: %v", err) - } - if s2.Properties["A"].Type == "mutated" { - t.Fatalf("ForWithMutation: expected A.Type to not be mutated") - } - if s2.Properties["B"].AdditionalProperties.Type == "mutated" { - t.Fatalf("ForWithMutation: expected B.AdditionalProperties.Type to not be mutated") - } - if s2.Properties["C"].Items.Type == "mutated" { - t.Fatalf("ForWithMutation: expected C.Items.Type to not be mutated") - } - if *s2.Properties["D"].MaxItems == 10 { - t.Fatalf("ForWithMutation: expected D.MaxItems to not be mutated") - } - if *s2.Properties["D"].MinItems == 10 { - t.Fatalf("ForWithMutation: expected D.MinItems to not be mutated") - } - if s2.Properties["E"].Types[0] == "mutated" { - t.Fatalf("ForWithMutation: expected E.Types[0] to not be mutated") - } - if s2.Required[0] == "mutated" { - t.Fatalf("ForWithMutation: expected Required[0] to not be mutated") - } -} - -type x struct { - Y y -} -type y struct { - X []x -} - -func TestForWithCycle(t *testing.T) { - type a []*a - type b1 struct{ b *b1 } // unexported field should be skipped - type b2 struct{ B *b2 } - type c1 struct{ c map[string]*c1 } // unexported field should be skipped - type c2 struct{ C map[string]*c2 } - - tests := []struct { - name string - shouldErr bool - fn func() error - }{ - {"slice alias (a)", true, func() error { _, err := jsonschema.For[a](nil); return err }}, - {"unexported self cycle (b1)", false, func() error { _, err := jsonschema.For[b1](nil); return err }}, - {"exported self cycle (b2)", true, func() error { _, err := jsonschema.For[b2](nil); return err }}, - {"unexported map self cycle (c1)", false, func() error { _, err := jsonschema.For[c1](nil); return err }}, - {"exported map self cycle (c2)", true, func() error { _, err := jsonschema.For[c2](nil); return err }}, - {"cross-cycle x -> y -> x", true, func() error { _, err := jsonschema.For[x](nil); return err }}, - {"cross-cycle y -> x -> y", true, func() error { _, err := jsonschema.For[y](nil); return err }}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - err := test.fn() - if test.shouldErr && err == nil { - t.Errorf("expected cycle error, got nil") - } - if !test.shouldErr && err != nil { - t.Errorf("unexpected error: %v", err) - } - }) - } -} - -func falseSchema() *jsonschema.Schema { - return &jsonschema.Schema{Not: &jsonschema.Schema{}} -} diff --git a/jsonschema/json_pointer.go b/jsonschema/json_pointer.go deleted file mode 100644 index d7eb4a9a..00000000 --- a/jsonschema/json_pointer.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// This file implements JSON Pointers. -// A JSON Pointer is a path that refers to one JSON value within another. -// If the path is empty, it refers to the root value. -// Otherwise, it is a sequence of slash-prefixed strings, like "/points/1/x", -// selecting successive properties (for JSON objects) or items (for JSON arrays). -// For example, when applied to this JSON value: -// { -// "points": [ -// {"x": 1, "y": 2}, -// {"x": 3, "y": 4} -// ] -// } -// -// the JSON Pointer "/points/1/x" refers to the number 3. -// See the spec at https://datatracker.ietf.org/doc/html/rfc6901. - -package jsonschema - -import ( - "errors" - "fmt" - "reflect" - "strconv" - "strings" -) - -var ( - jsonPointerEscaper = strings.NewReplacer("~", "~0", "/", "~1") - jsonPointerUnescaper = strings.NewReplacer("~0", "~", "~1", "/") -) - -func escapeJSONPointerSegment(s string) string { - return jsonPointerEscaper.Replace(s) -} - -func unescapeJSONPointerSegment(s string) string { - return jsonPointerUnescaper.Replace(s) -} - -// parseJSONPointer splits a JSON Pointer into a sequence of segments. It doesn't -// convert strings to numbers, because that depends on the traversal: a segment -// is treated as a number when applied to an array, but a string when applied to -// an object. See section 4 of the spec. -func parseJSONPointer(ptr string) (segments []string, err error) { - if ptr == "" { - return nil, nil - } - if ptr[0] != '/' { - return nil, fmt.Errorf("JSON Pointer %q does not begin with '/'", ptr) - } - // Unlike file paths, consecutive slashes are not coalesced. - // Split is nicer than Cut here, because it gets a final "/" right. - segments = strings.Split(ptr[1:], "/") - if strings.Contains(ptr, "~") { - // Undo the simple escaping rules that allow one to include a slash in a segment. - for i := range segments { - segments[i] = unescapeJSONPointerSegment(segments[i]) - } - } - return segments, nil -} - -// dereferenceJSONPointer returns the Schema that sptr points to within s, -// or an error if none. -// This implementation suffices for JSON Schema: pointers are applied only to Schemas, -// and refer only to Schemas. -func dereferenceJSONPointer(s *Schema, sptr string) (_ *Schema, err error) { - defer wrapf(&err, "JSON Pointer %q", sptr) - - segments, err := parseJSONPointer(sptr) - if err != nil { - return nil, err - } - v := reflect.ValueOf(s) - for _, seg := range segments { - switch v.Kind() { - case reflect.Pointer: - v = v.Elem() - if !v.IsValid() { - return nil, errors.New("navigated to nil reference") - } - fallthrough // if valid, can only be a pointer to a Schema - - case reflect.Struct: - // The segment must refer to a field in a Schema. - if v.Type() != reflect.TypeFor[Schema]() { - return nil, fmt.Errorf("navigated to non-Schema %s", v.Type()) - } - v = lookupSchemaField(v, seg) - if !v.IsValid() { - return nil, fmt.Errorf("no schema field %q", seg) - } - case reflect.Slice, reflect.Array: - // The segment must be an integer without leading zeroes that refers to an item in the - // slice or array. - if seg == "-" { - return nil, errors.New("the JSON Pointer array segment '-' is not supported") - } - if len(seg) > 1 && seg[0] == '0' { - return nil, fmt.Errorf("segment %q has leading zeroes", seg) - } - n, err := strconv.Atoi(seg) - if err != nil { - return nil, fmt.Errorf("invalid int: %q", seg) - } - if n < 0 || n >= v.Len() { - return nil, fmt.Errorf("index %d is out of bounds for array of length %d", n, v.Len()) - } - v = v.Index(n) - // Cannot be invalid. - case reflect.Map: - // The segment must be a key in the map. - v = v.MapIndex(reflect.ValueOf(seg)) - if !v.IsValid() { - return nil, fmt.Errorf("no key %q in map", seg) - } - default: - return nil, fmt.Errorf("value %s (%s) is not a schema, slice or map", v, v.Type()) - } - } - if s, ok := v.Interface().(*Schema); ok { - return s, nil - } - return nil, fmt.Errorf("does not refer to a schema, but to a %s", v.Type()) -} - -// lookupSchemaField returns the value of the field with the given name in v, -// or the zero value if there is no such field or it is not of type Schema or *Schema. -func lookupSchemaField(v reflect.Value, name string) reflect.Value { - if name == "type" { - // The "type" keyword may refer to Type or Types. - // At most one will be non-zero. - if t := v.FieldByName("Type"); !t.IsZero() { - return t - } - return v.FieldByName("Types") - } - if sf, ok := schemaFieldMap[name]; ok { - return v.FieldByIndex(sf.Index) - } - return reflect.Value{} -} diff --git a/jsonschema/json_pointer_test.go b/jsonschema/json_pointer_test.go deleted file mode 100644 index 54b84bed..00000000 --- a/jsonschema/json_pointer_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "strings" - "testing" -) - -func TestDereferenceJSONPointer(t *testing.T) { - s := &Schema{ - AllOf: []*Schema{{}, {}}, - Defs: map[string]*Schema{ - "": {Properties: map[string]*Schema{"": {}}}, - "A": {}, - "B": { - Defs: map[string]*Schema{ - "X": {}, - "Y": {}, - }, - }, - "/~": {}, - "~1": {}, - }, - } - - for _, tt := range []struct { - ptr string - want any - }{ - {"", s}, - {"/$defs/A", s.Defs["A"]}, - {"/$defs/B", s.Defs["B"]}, - {"/$defs/B/$defs/X", s.Defs["B"].Defs["X"]}, - {"/$defs//properties/", s.Defs[""].Properties[""]}, - {"/allOf/1", s.AllOf[1]}, - {"/$defs/~1~0", s.Defs["/~"]}, - {"/$defs/~01", s.Defs["~1"]}, - } { - got, err := dereferenceJSONPointer(s, tt.ptr) - if err != nil { - t.Fatal(err) - } - if got != tt.want { - t.Errorf("%s:\ngot %+v\nwant %+v", tt.ptr, got, tt.want) - } - } -} - -func TestDerefernceJSONPointerErrors(t *testing.T) { - s := &Schema{ - Type: "t", - Items: &Schema{}, - Required: []string{"a"}, - } - for _, tt := range []struct { - ptr string - want string // error must contain this string - }{ - {"x", "does not begin"}, // parse error: no initial '/' - {"/minItems", "does not refer to a schema"}, - {"/minItems/x", "navigated to nil"}, - {"/required/-", "not supported"}, - {"/required/01", "leading zeroes"}, - {"/required/x", "invalid int"}, - {"/required/1", "out of bounds"}, - {"/properties/x", "no key"}, - } { - _, err := dereferenceJSONPointer(s, tt.ptr) - if err == nil { - t.Errorf("%q: succeeded, want failure", tt.ptr) - } else if !strings.Contains(err.Error(), tt.want) { - t.Errorf("%q: error is %q, which does not contain %q", tt.ptr, err, tt.want) - } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/applicator.json b/jsonschema/meta-schemas/draft2020-12/meta/applicator.json deleted file mode 100644 index f4775974..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/applicator.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/applicator", - "$dynamicAnchor": "meta", - - "title": "Applicator vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "prefixItems": { "$ref": "#/$defs/schemaArray" }, - "items": { "$dynamicRef": "#meta" }, - "contains": { "$dynamicRef": "#meta" }, - "additionalProperties": { "$dynamicRef": "#meta" }, - "properties": { - "type": "object", - "additionalProperties": { "$dynamicRef": "#meta" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$dynamicRef": "#meta" }, - "propertyNames": { "format": "regex" }, - "default": {} - }, - "dependentSchemas": { - "type": "object", - "additionalProperties": { "$dynamicRef": "#meta" }, - "default": {} - }, - "propertyNames": { "$dynamicRef": "#meta" }, - "if": { "$dynamicRef": "#meta" }, - "then": { "$dynamicRef": "#meta" }, - "else": { "$dynamicRef": "#meta" }, - "allOf": { "$ref": "#/$defs/schemaArray" }, - "anyOf": { "$ref": "#/$defs/schemaArray" }, - "oneOf": { "$ref": "#/$defs/schemaArray" }, - "not": { "$dynamicRef": "#meta" } - }, - "$defs": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$dynamicRef": "#meta" } - } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/content.json b/jsonschema/meta-schemas/draft2020-12/meta/content.json deleted file mode 100644 index 76e3760d..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/content.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/content", - "$dynamicAnchor": "meta", - - "title": "Content vocabulary meta-schema", - - "type": ["object", "boolean"], - "properties": { - "contentEncoding": { "type": "string" }, - "contentMediaType": { "type": "string" }, - "contentSchema": { "$dynamicRef": "#meta" } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/core.json b/jsonschema/meta-schemas/draft2020-12/meta/core.json deleted file mode 100644 index 69186228..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/core.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/core", - "$dynamicAnchor": "meta", - - "title": "Core vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "$id": { - "$ref": "#/$defs/uriReferenceString", - "$comment": "Non-empty fragments not allowed.", - "pattern": "^[^#]*#?$" - }, - "$schema": { "$ref": "#/$defs/uriString" }, - "$ref": { "$ref": "#/$defs/uriReferenceString" }, - "$anchor": { "$ref": "#/$defs/anchorString" }, - "$dynamicRef": { "$ref": "#/$defs/uriReferenceString" }, - "$dynamicAnchor": { "$ref": "#/$defs/anchorString" }, - "$vocabulary": { - "type": "object", - "propertyNames": { "$ref": "#/$defs/uriString" }, - "additionalProperties": { - "type": "boolean" - } - }, - "$comment": { - "type": "string" - }, - "$defs": { - "type": "object", - "additionalProperties": { "$dynamicRef": "#meta" } - } - }, - "$defs": { - "anchorString": { - "type": "string", - "pattern": "^[A-Za-z_][-A-Za-z0-9._]*$" - }, - "uriString": { - "type": "string", - "format": "uri" - }, - "uriReferenceString": { - "type": "string", - "format": "uri-reference" - } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/format-annotation.json b/jsonschema/meta-schemas/draft2020-12/meta/format-annotation.json deleted file mode 100644 index 3479e669..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/format-annotation.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/format-annotation", - "$dynamicAnchor": "meta", - - "title": "Format vocabulary meta-schema for annotation results", - "type": ["object", "boolean"], - "properties": { - "format": { "type": "string" } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/meta-data.json b/jsonschema/meta-schemas/draft2020-12/meta/meta-data.json deleted file mode 100644 index 4049ab21..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/meta-data.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/meta-data", - "$dynamicAnchor": "meta", - - "title": "Meta-data vocabulary meta-schema", - - "type": ["object", "boolean"], - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": true, - "deprecated": { - "type": "boolean", - "default": false - }, - "readOnly": { - "type": "boolean", - "default": false - }, - "writeOnly": { - "type": "boolean", - "default": false - }, - "examples": { - "type": "array", - "items": true - } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/unevaluated.json b/jsonschema/meta-schemas/draft2020-12/meta/unevaluated.json deleted file mode 100644 index 93779e54..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/unevaluated.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/unevaluated", - "$dynamicAnchor": "meta", - - "title": "Unevaluated applicator vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "unevaluatedItems": { "$dynamicRef": "#meta" }, - "unevaluatedProperties": { "$dynamicRef": "#meta" } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/meta/validation.json b/jsonschema/meta-schemas/draft2020-12/meta/validation.json deleted file mode 100644 index ebb75db7..00000000 --- a/jsonschema/meta-schemas/draft2020-12/meta/validation.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/meta/validation", - "$dynamicAnchor": "meta", - - "title": "Validation vocabulary meta-schema", - "type": ["object", "boolean"], - "properties": { - "type": { - "anyOf": [ - { "$ref": "#/$defs/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/$defs/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "const": true, - "enum": { - "type": "array", - "items": true - }, - "multipleOf": { - "type": "number", - "exclusiveMinimum": 0 - }, - "maximum": { - "type": "number" - }, - "exclusiveMaximum": { - "type": "number" - }, - "minimum": { - "type": "number" - }, - "exclusiveMinimum": { - "type": "number" - }, - "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, - "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "pattern": { - "type": "string", - "format": "regex" - }, - "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, - "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "uniqueItems": { - "type": "boolean", - "default": false - }, - "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, - "minContains": { - "$ref": "#/$defs/nonNegativeInteger", - "default": 1 - }, - "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, - "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, - "required": { "$ref": "#/$defs/stringArray" }, - "dependentRequired": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/stringArray" - } - } - }, - "$defs": { - "nonNegativeInteger": { - "type": "integer", - "minimum": 0 - }, - "nonNegativeIntegerDefault0": { - "$ref": "#/$defs/nonNegativeInteger", - "default": 0 - }, - "simpleTypes": { - "enum": [ - "array", - "boolean", - "integer", - "null", - "number", - "object", - "string" - ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "uniqueItems": true, - "default": [] - } - } -} diff --git a/jsonschema/meta-schemas/draft2020-12/schema.json b/jsonschema/meta-schemas/draft2020-12/schema.json deleted file mode 100644 index d5e2d31c..00000000 --- a/jsonschema/meta-schemas/draft2020-12/schema.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://json-schema.org/draft/2020-12/schema", - "$vocabulary": { - "https://json-schema.org/draft/2020-12/vocab/core": true, - "https://json-schema.org/draft/2020-12/vocab/applicator": true, - "https://json-schema.org/draft/2020-12/vocab/unevaluated": true, - "https://json-schema.org/draft/2020-12/vocab/validation": true, - "https://json-schema.org/draft/2020-12/vocab/meta-data": true, - "https://json-schema.org/draft/2020-12/vocab/format-annotation": true, - "https://json-schema.org/draft/2020-12/vocab/content": true - }, - "$dynamicAnchor": "meta", - - "title": "Core and Validation specifications meta-schema", - "allOf": [ - {"$ref": "meta/core"}, - {"$ref": "meta/applicator"}, - {"$ref": "meta/unevaluated"}, - {"$ref": "meta/validation"}, - {"$ref": "meta/meta-data"}, - {"$ref": "meta/format-annotation"}, - {"$ref": "meta/content"} - ], - "type": ["object", "boolean"], - "$comment": "This meta-schema also defines keywords that have appeared in previous drafts in order to prevent incompatible extensions as they remain in common use.", - "properties": { - "definitions": { - "$comment": "\"definitions\" has been replaced by \"$defs\".", - "type": "object", - "additionalProperties": { "$dynamicRef": "#meta" }, - "deprecated": true, - "default": {} - }, - "dependencies": { - "$comment": "\"dependencies\" has been split and replaced by \"dependentSchemas\" and \"dependentRequired\" in order to serve their differing semantics.", - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$dynamicRef": "#meta" }, - { "$ref": "meta/validation#/$defs/stringArray" } - ] - }, - "deprecated": true, - "default": {} - }, - "$recursiveAnchor": { - "$comment": "\"$recursiveAnchor\" has been replaced by \"$dynamicAnchor\".", - "$ref": "meta/core#/$defs/anchorString", - "deprecated": true - }, - "$recursiveRef": { - "$comment": "\"$recursiveRef\" has been replaced by \"$dynamicRef\".", - "$ref": "meta/core#/$defs/uriReferenceString", - "deprecated": true - } - } -} diff --git a/jsonschema/resolve.go b/jsonschema/resolve.go deleted file mode 100644 index cc551e79..00000000 --- a/jsonschema/resolve.go +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -// This file deals with preparing a schema for validation, including various checks, -// optimizations, and the resolution of cross-schema references. - -package jsonschema - -import ( - "errors" - "fmt" - "net/url" - "reflect" - "regexp" - "strings" -) - -// A Resolved consists of a [Schema] along with associated information needed to -// validate documents against it. -// A Resolved has been validated against its meta-schema, and all its references -// (the $ref and $dynamicRef keywords) have been resolved to their referenced Schemas. -// Call [Schema.Resolve] to obtain a Resolved from a Schema. -type Resolved struct { - root *Schema - // map from $ids to their schemas - resolvedURIs map[string]*Schema - // map from schemas to additional info computed during resolution - resolvedInfos map[*Schema]*resolvedInfo -} - -func newResolved(s *Schema) *Resolved { - return &Resolved{ - root: s, - resolvedURIs: map[string]*Schema{}, - resolvedInfos: map[*Schema]*resolvedInfo{}, - } -} - -// resolvedInfo holds information specific to a schema that is computed by [Schema.Resolve]. -type resolvedInfo struct { - s *Schema - // The JSON Pointer path from the root schema to here. - // Used in errors. - path string - // The schema's base schema. - // If the schema is the root or has an ID, its base is itself. - // Otherwise, its base is the innermost enclosing schema whose base - // is itself. - // Intuitively, a base schema is one that can be referred to with a - // fragmentless URI. - base *Schema - // The URI for the schema, if it is the root or has an ID. - // Otherwise nil. - // Invariants: - // s.base.uri != nil. - // s.base == s <=> s.uri != nil - uri *url.URL - // The schema to which Ref refers. - resolvedRef *Schema - - // If the schema has a dynamic ref, exactly one of the next two fields - // will be non-zero after successful resolution. - // The schema to which the dynamic ref refers when it acts lexically. - resolvedDynamicRef *Schema - // The anchor to look up on the stack when the dynamic ref acts dynamically. - dynamicRefAnchor string - - // The following fields are independent of arguments to Schema.Resolved, - // so they could live on the Schema. We put them here for simplicity. - - // The set of required properties. - isRequired map[string]bool - - // Compiled regexps. - pattern *regexp.Regexp - patternProperties map[*regexp.Regexp]*Schema - - // Map from anchors to subschemas. - anchors map[string]anchorInfo -} - -// Schema returns the schema that was resolved. -// It must not be modified. -func (r *Resolved) Schema() *Schema { return r.root } - -// schemaString returns a short string describing the schema. -func (r *Resolved) schemaString(s *Schema) string { - if s.ID != "" { - return s.ID - } - info := r.resolvedInfos[s] - if info.path != "" { - return info.path - } - return "" -} - -// A Loader reads and unmarshals the schema at uri, if any. -type Loader func(uri *url.URL) (*Schema, error) - -// ResolveOptions are options for [Schema.Resolve]. -type ResolveOptions struct { - // BaseURI is the URI relative to which the root schema should be resolved. - // If non-empty, must be an absolute URI (one that starts with a scheme). - // It is resolved (in the URI sense; see [url.ResolveReference]) with root's - // $id property. - // If the resulting URI is not absolute, then the schema cannot contain - // relative URI references. - BaseURI string - // Loader loads schemas that are referred to by a $ref but are not under the - // root schema (remote references). - // If nil, resolving a remote reference will return an error. - Loader Loader - // ValidateDefaults determines whether to validate values of "default" keywords - // against their schemas. - // The [JSON Schema specification] does not require this, but it is - // recommended if defaults will be used. - // - // [JSON Schema specification]: https://json-schema.org/understanding-json-schema/reference/annotations - ValidateDefaults bool -} - -// Resolve resolves all references within the schema and performs other tasks that -// prepare the schema for validation. -// If opts is nil, the default values are used. -// The schema must not be changed after Resolve is called. -// The same schema may be resolved multiple times. -func (root *Schema) Resolve(opts *ResolveOptions) (*Resolved, error) { - // There are up to five steps required to prepare a schema to validate. - // 1. Load: read the schema from somewhere and unmarshal it. - // This schema (root) may have been loaded or created in memory, but other schemas that - // come into the picture in step 4 will be loaded by the given loader. - // 2. Check: validate the schema against a meta-schema, and perform other well-formedness checks. - // Precompute some values along the way. - // 3. Resolve URIs: determine the base URI of the root and all its subschemas, and - // resolve (in the URI sense) all identifiers and anchors with their bases. This step results - // in a map from URIs to schemas within root. - // 4. Resolve references: all refs in the schemas are replaced with the schema they refer to. - // 5. (Optional.) If opts.ValidateDefaults is true, validate the defaults. - r := &resolver{loaded: map[string]*Resolved{}} - if opts != nil { - r.opts = *opts - } - var base *url.URL - if r.opts.BaseURI == "" { - base = &url.URL{} // so we can call ResolveReference on it - } else { - var err error - base, err = url.Parse(r.opts.BaseURI) - if err != nil { - return nil, fmt.Errorf("parsing base URI: %w", err) - } - } - - if r.opts.Loader == nil { - r.opts.Loader = func(uri *url.URL) (*Schema, error) { - return nil, errors.New("cannot resolve remote schemas: no loader passed to Schema.Resolve") - } - } - - resolved, err := r.resolve(root, base) - if err != nil { - return nil, err - } - if r.opts.ValidateDefaults { - if err := resolved.validateDefaults(); err != nil { - return nil, err - } - } - // TODO: before we return, throw away anything we don't need for validation. - return resolved, nil -} - -// A resolver holds the state for resolution. -type resolver struct { - opts ResolveOptions - // A cache of loaded and partly resolved schemas. (They may not have had their - // refs resolved.) The cache ensures that the loader will never be called more - // than once with the same URI, and that reference cycles are handled properly. - loaded map[string]*Resolved -} - -func (r *resolver) resolve(s *Schema, baseURI *url.URL) (*Resolved, error) { - if baseURI.Fragment != "" { - return nil, fmt.Errorf("base URI %s must not have a fragment", baseURI) - } - rs := newResolved(s) - - if err := s.check(rs.resolvedInfos); err != nil { - return nil, err - } - - if err := resolveURIs(rs, baseURI); err != nil { - return nil, err - } - - // Remember the schema by both the URI we loaded it from and its canonical name, - // which may differ if the schema has an $id. - // We must set the map before calling resolveRefs, or ref cycles will cause unbounded recursion. - r.loaded[baseURI.String()] = rs - r.loaded[rs.resolvedInfos[s].uri.String()] = rs - - if err := r.resolveRefs(rs); err != nil { - return nil, err - } - return rs, nil -} - -func (root *Schema) check(infos map[*Schema]*resolvedInfo) error { - // Check for structural validity. Do this first and fail fast: - // bad structure will cause other code to panic. - if err := root.checkStructure(infos); err != nil { - return err - } - - var errs []error - report := func(err error) { errs = append(errs, err) } - - for ss := range root.all() { - ss.checkLocal(report, infos) - } - return errors.Join(errs...) -} - -// checkStructure verifies that root and its subschemas form a tree. -// It also assigns each schema a unique path, to improve error messages. -func (root *Schema) checkStructure(infos map[*Schema]*resolvedInfo) error { - assert(len(infos) == 0, "non-empty infos") - - var check func(reflect.Value, []byte) error - check = func(v reflect.Value, path []byte) error { - // For the purpose of error messages, the root schema has path "root" - // and other schemas' paths are their JSON Pointer from the root. - p := "root" - if len(path) > 0 { - p = string(path) - } - s := v.Interface().(*Schema) - if s == nil { - return fmt.Errorf("jsonschema: schema at %s is nil", p) - } - if info, ok := infos[s]; ok { - // We've seen s before. - // The schema graph at root is not a tree, but it needs to - // be because a schema's base must be unique. - // A cycle would also put Schema.all into an infinite recursion. - return fmt.Errorf("jsonschema: schemas at %s do not form a tree; %s appears more than once (also at %s)", - root, info.path, p) - } - infos[s] = &resolvedInfo{s: s, path: p} - - for _, info := range schemaFieldInfos { - fv := v.Elem().FieldByIndex(info.sf.Index) - switch info.sf.Type { - case schemaType: - // A field that contains an individual schema. - // A nil is valid: it just means the field isn't present. - if !fv.IsNil() { - if err := check(fv, fmt.Appendf(path, "/%s", info.jsonName)); err != nil { - return err - } - } - - case schemaSliceType: - for i := range fv.Len() { - if err := check(fv.Index(i), fmt.Appendf(path, "/%s/%d", info.jsonName, i)); err != nil { - return err - } - } - - case schemaMapType: - iter := fv.MapRange() - for iter.Next() { - key := escapeJSONPointerSegment(iter.Key().String()) - if err := check(iter.Value(), fmt.Appendf(path, "/%s/%s", info.jsonName, key)); err != nil { - return err - } - } - } - - } - return nil - } - - return check(reflect.ValueOf(root), make([]byte, 0, 256)) -} - -// checkLocal checks s for validity, independently of other schemas it may refer to. -// Since checking a regexp involves compiling it, checkLocal saves those compiled regexps -// in the schema for later use. -// It appends the errors it finds to errs. -func (s *Schema) checkLocal(report func(error), infos map[*Schema]*resolvedInfo) { - addf := func(format string, args ...any) { - msg := fmt.Sprintf(format, args...) - report(fmt.Errorf("jsonschema.Schema: %s: %s", s, msg)) - } - - if s == nil { - addf("nil subschema") - return - } - if err := s.basicChecks(); err != nil { - report(err) - return - } - - // TODO: validate the schema's properties, - // ideally by jsonschema-validating it against the meta-schema. - - // Some properties are present so that Schemas can round-trip, but we do not - // validate them. - // Currently, it's just the $vocabulary property. - // As a special case, we can validate the 2020-12 meta-schema. - if s.Vocabulary != nil && s.Schema != draft202012 { - addf("cannot validate a schema with $vocabulary") - } - - info := infos[s] - - // Check and compile regexps. - if s.Pattern != "" { - re, err := regexp.Compile(s.Pattern) - if err != nil { - addf("pattern: %v", err) - } else { - info.pattern = re - } - } - if len(s.PatternProperties) > 0 { - info.patternProperties = map[*regexp.Regexp]*Schema{} - for reString, subschema := range s.PatternProperties { - re, err := regexp.Compile(reString) - if err != nil { - addf("patternProperties[%q]: %v", reString, err) - continue - } - info.patternProperties[re] = subschema - } - } - - // Build a set of required properties, to avoid quadratic behavior when validating - // a struct. - if len(s.Required) > 0 { - info.isRequired = map[string]bool{} - for _, r := range s.Required { - info.isRequired[r] = true - } - } -} - -// resolveURIs resolves the ids and anchors in all the schemas of root, relative -// to baseURI. -// See https://json-schema.org/draft/2020-12/json-schema-core#section-8.2, section -// 8.2.1. -// -// Every schema has a base URI and a parent base URI. -// -// The parent base URI is the base URI of the lexically enclosing schema, or for -// a root schema, the URI it was loaded from or the one supplied to [Schema.Resolve]. -// -// If the schema has no $id property, the base URI of a schema is that of its parent. -// If the schema does have an $id, it must be a URI, possibly relative. The schema's -// base URI is the $id resolved (in the sense of [url.URL.ResolveReference]) against -// the parent base. -// -// As an example, consider this schema loaded from http://a.com/root.json (quotes omitted): -// -// { -// allOf: [ -// {$id: "sub1.json", minLength: 5}, -// {$id: "http://b.com", minimum: 10}, -// {not: {maximum: 20}} -// ] -// } -// -// The base URIs are as follows. Schema locations are expressed in the JSON Pointer notation. -// -// schema base URI -// root http://a.com/root.json -// allOf/0 http://a.com/sub1.json -// allOf/1 http://b.com (absolute $id; doesn't matter that it's not under the loaded URI) -// allOf/2 http://a.com/root.json (inherited from parent) -// allOf/2/not http://a.com/root.json (inherited from parent) -func resolveURIs(rs *Resolved, baseURI *url.URL) error { - var resolve func(s, base *Schema) error - resolve = func(s, base *Schema) error { - info := rs.resolvedInfos[s] - baseInfo := rs.resolvedInfos[base] - - // ids are scoped to the root. - if s.ID != "" { - // A non-empty ID establishes a new base. - idURI, err := url.Parse(s.ID) - if err != nil { - return err - } - if idURI.Fragment != "" { - return fmt.Errorf("$id %s must not have a fragment", s.ID) - } - // The base URI for this schema is its $id resolved against the parent base. - info.uri = baseInfo.uri.ResolveReference(idURI) - if !info.uri.IsAbs() { - return fmt.Errorf("$id %s does not resolve to an absolute URI (base is %q)", s.ID, baseInfo.uri) - } - rs.resolvedURIs[info.uri.String()] = s - base = s // needed for anchors - baseInfo = rs.resolvedInfos[base] - } - info.base = base - - // Anchors and dynamic anchors are URI fragments that are scoped to their base. - // We treat them as keys in a map stored within the schema. - setAnchor := func(anchor string, dynamic bool) error { - if anchor != "" { - if _, ok := baseInfo.anchors[anchor]; ok { - return fmt.Errorf("duplicate anchor %q in %s", anchor, baseInfo.uri) - } - if baseInfo.anchors == nil { - baseInfo.anchors = map[string]anchorInfo{} - } - baseInfo.anchors[anchor] = anchorInfo{s, dynamic} - } - return nil - } - - setAnchor(s.Anchor, false) - setAnchor(s.DynamicAnchor, true) - - for c := range s.children() { - if err := resolve(c, base); err != nil { - return err - } - } - return nil - } - - // Set the root URI to the base for now. If the root has an $id, this will change. - rs.resolvedInfos[rs.root].uri = baseURI - // The original base, even if changed, is still a valid way to refer to the root. - rs.resolvedURIs[baseURI.String()] = rs.root - - return resolve(rs.root, rs.root) -} - -// resolveRefs replaces every ref in the schemas with the schema it refers to. -// A reference that doesn't resolve within the schema may refer to some other schema -// that needs to be loaded. -func (r *resolver) resolveRefs(rs *Resolved) error { - for s := range rs.root.all() { - info := rs.resolvedInfos[s] - if s.Ref != "" { - refSchema, _, err := r.resolveRef(rs, s, s.Ref) - if err != nil { - return err - } - // Whether or not the anchor referred to by $ref fragment is dynamic, - // the ref still treats it lexically. - info.resolvedRef = refSchema - } - if s.DynamicRef != "" { - refSchema, frag, err := r.resolveRef(rs, s, s.DynamicRef) - if err != nil { - return err - } - if frag != "" { - // The dynamic ref's fragment points to a dynamic anchor. - // We must resolve the fragment at validation time. - info.dynamicRefAnchor = frag - } else { - // There is no dynamic anchor in the lexically referenced schema, - // so the dynamic ref behaves like a lexical ref. - info.resolvedDynamicRef = refSchema - } - } - } - return nil -} - -// resolveRef resolves the reference ref, which is either s.Ref or s.DynamicRef. -func (r *resolver) resolveRef(rs *Resolved, s *Schema, ref string) (_ *Schema, dynamicFragment string, err error) { - refURI, err := url.Parse(ref) - if err != nil { - return nil, "", err - } - // URI-resolve the ref against the current base URI to get a complete URI. - base := rs.resolvedInfos[s].base - refURI = rs.resolvedInfos[base].uri.ResolveReference(refURI) - // The non-fragment part of a ref URI refers to the base URI of some schema. - // This part is the same for dynamic refs too: their non-fragment part resolves - // lexically. - u := *refURI - u.Fragment = "" - fraglessRefURI := &u - // Look it up locally. - referencedSchema := rs.resolvedURIs[fraglessRefURI.String()] - if referencedSchema == nil { - // The schema is remote. Maybe we've already loaded it. - // We assume that the non-fragment part of refURI refers to a top-level schema - // document. That is, we don't support the case exemplified by - // http://foo.com/bar.json/baz, where the document is in bar.json and - // the reference points to a subschema within it. - // TODO: support that case. - if lrs := r.loaded[fraglessRefURI.String()]; lrs != nil { - referencedSchema = lrs.root - } else { - // Try to load the schema. - ls, err := r.opts.Loader(fraglessRefURI) - if err != nil { - return nil, "", fmt.Errorf("loading %s: %w", fraglessRefURI, err) - } - lrs, err := r.resolve(ls, fraglessRefURI) - if err != nil { - return nil, "", err - } - referencedSchema = lrs.root - assert(referencedSchema != nil, "nil referenced schema") - // Copy the resolvedInfos from lrs into rs, without overwriting - // (hence we can't use maps.Insert). - for s, i := range lrs.resolvedInfos { - if rs.resolvedInfos[s] == nil { - rs.resolvedInfos[s] = i - } - } - } - } - - frag := refURI.Fragment - // Look up frag in refSchema. - // frag is either a JSON Pointer or the name of an anchor. - // A JSON Pointer is either the empty string or begins with a '/', - // whereas anchors are always non-empty strings that don't contain slashes. - if frag != "" && !strings.HasPrefix(frag, "/") { - resInfo := rs.resolvedInfos[referencedSchema] - info, found := resInfo.anchors[frag] - - if !found { - return nil, "", fmt.Errorf("no anchor %q in %s", frag, s) - } - if info.dynamic { - dynamicFragment = frag - } - return info.schema, dynamicFragment, nil - } - // frag is a JSON Pointer. - s, err = dereferenceJSONPointer(referencedSchema, frag) - return s, "", err -} diff --git a/jsonschema/resolve_test.go b/jsonschema/resolve_test.go deleted file mode 100644 index 36aa424b..00000000 --- a/jsonschema/resolve_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "errors" - "maps" - "net/url" - "regexp" - "slices" - "strings" - "testing" -) - -func TestSchemaStructure(t *testing.T) { - check := func(s *Schema, want string) { - t.Helper() - infos := map[*Schema]*resolvedInfo{} - err := s.checkStructure(infos) - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("checkStructure returned error %q, want %q", err, want) - } - } - - dag := &Schema{Type: "number"} - dag = &Schema{Items: dag, Contains: dag} - check(dag, "do not form a tree") - - tree := &Schema{Type: "number"} - tree.Items = tree - check(tree, "do not form a tree") - - sliceNil := &Schema{PrefixItems: []*Schema{nil}} - check(sliceNil, "is nil") - - sliceMap := &Schema{Properties: map[string]*Schema{"a": nil}} - check(sliceMap, "is nil") -} - -func TestCheckLocal(t *testing.T) { - for _, tt := range []struct { - s *Schema - want string // error must be non-nil and match this regexp - }{ - { - &Schema{Pattern: "]["}, - "regexp", - }, - { - &Schema{PatternProperties: map[string]*Schema{"*": {}}}, - "regexp", - }, - } { - _, err := tt.s.Resolve(nil) - if err == nil { - t.Errorf("%s: unexpectedly passed", tt.s.json()) - continue - } - if !regexp.MustCompile(tt.want).MatchString(err.Error()) { - t.Errorf("checkLocal returned error\n%q\nwanted it to match\n%s\nregexp: %s", - tt.s.json(), err, tt.want) - } - } -} - -func TestPaths(t *testing.T) { - // CheckStructure should assign paths to schemas. - // This test also verifies that Schema.all visits maps in sorted order. - root := &Schema{ - Type: "string", - PrefixItems: []*Schema{{Type: "int"}, {Items: &Schema{Type: "null"}}}, - Contains: &Schema{Properties: map[string]*Schema{ - "~1": {Type: "boolean"}, - "p": {}, - }}, - } - - type item struct { - s *Schema - p string - } - want := []item{ - {root, "root"}, - {root.Contains, "/contains"}, - {root.Contains.Properties["p"], "/contains/properties/p"}, - {root.Contains.Properties["~1"], "/contains/properties/~01"}, - {root.PrefixItems[0], "/prefixItems/0"}, - {root.PrefixItems[1], "/prefixItems/1"}, - {root.PrefixItems[1].Items, "/prefixItems/1/items"}, - } - rs := newResolved(root) - if err := root.checkStructure(rs.resolvedInfos); err != nil { - t.Fatal(err) - } - - var got []item - for s := range root.all() { - got = append(got, item{s, rs.resolvedInfos[s].path}) - } - if !slices.Equal(got, want) { - t.Errorf("\ngot %v\nwant %v", got, want) - } -} - -func TestResolveURIs(t *testing.T) { - for _, baseURI := range []string{"", "http://a.com"} { - t.Run(baseURI, func(t *testing.T) { - root := &Schema{ - ID: "http://b.com", - Items: &Schema{ - ID: "/foo.json", - }, - Contains: &Schema{ - ID: "/bar.json", - Anchor: "a", - DynamicAnchor: "da", - Items: &Schema{ - Anchor: "b", - Items: &Schema{ - // An ID shouldn't be a query param, but this tests - // resolving an ID with its parent. - ID: "?items", - Anchor: "c", - }, - }, - }, - } - base, err := url.Parse(baseURI) - if err != nil { - t.Fatal(err) - } - - rs := newResolved(root) - if err := root.check(rs.resolvedInfos); err != nil { - t.Fatal(err) - } - if err := resolveURIs(rs, base); err != nil { - t.Fatal(err) - } - - wantIDs := map[string]*Schema{ - baseURI: root, - "http://b.com/foo.json": root.Items, - "http://b.com/bar.json": root.Contains, - "http://b.com/bar.json?items": root.Contains.Items.Items, - } - if baseURI != root.ID { - wantIDs[root.ID] = root - } - wantAnchors := map[*Schema]map[string]anchorInfo{ - root.Contains: { - "a": anchorInfo{root.Contains, false}, - "da": anchorInfo{root.Contains, true}, - "b": anchorInfo{root.Contains.Items, false}, - }, - root.Contains.Items.Items: { - "c": anchorInfo{root.Contains.Items.Items, false}, - }, - } - - got := rs.resolvedURIs - gotKeys := slices.Sorted(maps.Keys(got)) - wantKeys := slices.Sorted(maps.Keys(wantIDs)) - if !slices.Equal(gotKeys, wantKeys) { - t.Errorf("ID keys:\ngot %q\nwant %q", gotKeys, wantKeys) - } - if !maps.Equal(got, wantIDs) { - t.Errorf("IDs:\ngot %+v\n\nwant %+v", got, wantIDs) - } - for s := range root.all() { - info := rs.resolvedInfos[s] - if want := wantAnchors[s]; want != nil { - if got := info.anchors; !maps.Equal(got, want) { - t.Errorf("anchors:\ngot %+v\n\nwant %+v", got, want) - } - } else if info.anchors != nil { - t.Errorf("non-nil anchors for %s", s) - } - } - }) - } -} - -func TestRefCycle(t *testing.T) { - // Verify that cycles of refs are OK. - // The test suite doesn't check this, surprisingly. - schemas := map[string]*Schema{ - "root": {Ref: "a"}, - "a": {Ref: "b"}, - "b": {Ref: "a"}, - } - - loader := func(uri *url.URL) (*Schema, error) { - s, ok := schemas[uri.Path[1:]] - if !ok { - return nil, errors.New("not found") - } - return s, nil - } - - rs, err := schemas["root"].Resolve(&ResolveOptions{Loader: loader}) - if err != nil { - t.Fatal(err) - } - - check := func(s *Schema, key string) { - t.Helper() - if rs.resolvedInfos[s].resolvedRef != schemas[key] { - t.Errorf("%s resolvedRef != schemas[%q]", s.json(), key) - } - } - - check(rs.root, "a") - check(schemas["a"], "b") - check(schemas["b"], "a") -} diff --git a/jsonschema/schema.go b/jsonschema/schema.go deleted file mode 100644 index 9a68cd5d..00000000 --- a/jsonschema/schema.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "bytes" - "cmp" - "encoding/json" - "errors" - "fmt" - "iter" - "maps" - "math" - "reflect" - "slices" -) - -// A Schema is a JSON schema object. -// It corresponds to the 2020-12 draft, as described in https://json-schema.org/draft/2020-12, -// specifically: -// - https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01 -// - https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01 -// -// A Schema value may have non-zero values for more than one field: -// all relevant non-zero fields are used for validation. -// There is one exception to provide more Go type-safety: the Type and Types fields -// are mutually exclusive. -// -// Since this struct is a Go representation of a JSON value, it inherits JSON's -// distinction between nil and empty. Nil slices and maps are considered absent, -// but empty ones are present and affect validation. For example, -// -// Schema{Enum: nil} -// -// is equivalent to an empty schema, so it validates every instance. But -// -// Schema{Enum: []any{}} -// -// requires equality to some slice element, so it vacuously rejects every instance. -type Schema struct { - // core - ID string `json:"$id,omitempty"` - Schema string `json:"$schema,omitempty"` - Ref string `json:"$ref,omitempty"` - Comment string `json:"$comment,omitempty"` - Defs map[string]*Schema `json:"$defs,omitempty"` - // definitions is deprecated but still allowed. It is a synonym for $defs. - Definitions map[string]*Schema `json:"definitions,omitempty"` - - Anchor string `json:"$anchor,omitempty"` - DynamicAnchor string `json:"$dynamicAnchor,omitempty"` - DynamicRef string `json:"$dynamicRef,omitempty"` - Vocabulary map[string]bool `json:"$vocabulary,omitempty"` - - // metadata - Title string `json:"title,omitempty"` - Description string `json:"description,omitempty"` - Default json.RawMessage `json:"default,omitempty"` - Deprecated bool `json:"deprecated,omitempty"` - ReadOnly bool `json:"readOnly,omitempty"` - WriteOnly bool `json:"writeOnly,omitempty"` - Examples []any `json:"examples,omitempty"` - - // validation - // Use Type for a single type, or Types for multiple types; never both. - Type string `json:"-"` - Types []string `json:"-"` - Enum []any `json:"enum,omitempty"` - // Const is *any because a JSON null (Go nil) is a valid value. - Const *any `json:"const,omitempty"` - MultipleOf *float64 `json:"multipleOf,omitempty"` - Minimum *float64 `json:"minimum,omitempty"` - Maximum *float64 `json:"maximum,omitempty"` - ExclusiveMinimum *float64 `json:"exclusiveMinimum,omitempty"` - ExclusiveMaximum *float64 `json:"exclusiveMaximum,omitempty"` - MinLength *int `json:"minLength,omitempty"` - MaxLength *int `json:"maxLength,omitempty"` - Pattern string `json:"pattern,omitempty"` - - // arrays - PrefixItems []*Schema `json:"prefixItems,omitempty"` - Items *Schema `json:"items,omitempty"` - MinItems *int `json:"minItems,omitempty"` - MaxItems *int `json:"maxItems,omitempty"` - AdditionalItems *Schema `json:"additionalItems,omitempty"` - UniqueItems bool `json:"uniqueItems,omitempty"` - Contains *Schema `json:"contains,omitempty"` - MinContains *int `json:"minContains,omitempty"` // *int, not int: default is 1, not 0 - MaxContains *int `json:"maxContains,omitempty"` - UnevaluatedItems *Schema `json:"unevaluatedItems,omitempty"` - - // objects - MinProperties *int `json:"minProperties,omitempty"` - MaxProperties *int `json:"maxProperties,omitempty"` - Required []string `json:"required,omitempty"` - DependentRequired map[string][]string `json:"dependentRequired,omitempty"` - Properties map[string]*Schema `json:"properties,omitempty"` - PatternProperties map[string]*Schema `json:"patternProperties,omitempty"` - AdditionalProperties *Schema `json:"additionalProperties,omitempty"` - PropertyNames *Schema `json:"propertyNames,omitempty"` - UnevaluatedProperties *Schema `json:"unevaluatedProperties,omitempty"` - - // logic - AllOf []*Schema `json:"allOf,omitempty"` - AnyOf []*Schema `json:"anyOf,omitempty"` - OneOf []*Schema `json:"oneOf,omitempty"` - Not *Schema `json:"not,omitempty"` - - // conditional - If *Schema `json:"if,omitempty"` - Then *Schema `json:"then,omitempty"` - Else *Schema `json:"else,omitempty"` - DependentSchemas map[string]*Schema `json:"dependentSchemas,omitempty"` - - // other - // https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.8 - ContentEncoding string `json:"contentEncoding,omitempty"` - ContentMediaType string `json:"contentMediaType,omitempty"` - ContentSchema *Schema `json:"contentSchema,omitempty"` - - // https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.7 - Format string `json:"format,omitempty"` - - // Extra allows for additional keywords beyond those specified. - Extra map[string]any `json:"-"` -} - -// falseSchema returns a new Schema tree that fails to validate any value. -func falseSchema() *Schema { - return &Schema{Not: &Schema{}} -} - -// anchorInfo records the subschema to which an anchor refers, and whether -// the anchor keyword is $anchor or $dynamicAnchor. -type anchorInfo struct { - schema *Schema - dynamic bool -} - -// String returns a short description of the schema. -func (s *Schema) String() string { - if s.ID != "" { - return s.ID - } - if a := cmp.Or(s.Anchor, s.DynamicAnchor); a != "" { - return fmt.Sprintf("anchor %s", a) - } - return "" -} - -// CloneSchemas returns a copy of s. -// The copy is shallow except for sub-schemas, which are themelves copied with CloneSchemas. -// This allows both s and s.CloneSchemas() to appear as sub-schemas in the same parent. -func (s *Schema) CloneSchemas() *Schema { - if s == nil { - return nil - } - s2 := *s - v := reflect.ValueOf(&s2) - for _, info := range schemaFieldInfos { - fv := v.Elem().FieldByIndex(info.sf.Index) - switch info.sf.Type { - case schemaType: - sscss := fv.Interface().(*Schema) - fv.Set(reflect.ValueOf(sscss.CloneSchemas())) - - case schemaSliceType: - slice := fv.Interface().([]*Schema) - slice = slices.Clone(slice) - for i, ss := range slice { - slice[i] = ss.CloneSchemas() - } - fv.Set(reflect.ValueOf(slice)) - - case schemaMapType: - m := fv.Interface().(map[string]*Schema) - m = maps.Clone(m) - for k, ss := range m { - m[k] = ss.CloneSchemas() - } - fv.Set(reflect.ValueOf(m)) - } - } - return &s2 -} - -func (s *Schema) basicChecks() error { - if s.Type != "" && s.Types != nil { - return errors.New("both Type and Types are set; at most one should be") - } - if s.Defs != nil && s.Definitions != nil { - return errors.New("both Defs and Definitions are set; at most one should be") - } - return nil -} - -type schemaWithoutMethods Schema // doesn't implement json.{Unm,M}arshaler - -func (s *Schema) MarshalJSON() ([]byte, error) { - if err := s.basicChecks(); err != nil { - return nil, err - } - - // Marshal either Type or Types as "type". - var typ any - switch { - case s.Type != "": - typ = s.Type - case s.Types != nil: - typ = s.Types - } - ms := struct { - Type any `json:"type,omitempty"` - *schemaWithoutMethods - }{ - Type: typ, - schemaWithoutMethods: (*schemaWithoutMethods)(s), - } - bs, err := marshalStructWithMap(&ms, "Extra") - if err != nil { - return nil, err - } - // Marshal {} as true and {"not": {}} as false. - // It is wasteful to do this here instead of earlier, but much easier. - switch { - case bytes.Equal(bs, []byte(`{}`)): - bs = []byte("true") - case bytes.Equal(bs, []byte(`{"not":true}`)): - bs = []byte("false") - } - return bs, nil -} - -func (s *Schema) UnmarshalJSON(data []byte) error { - // A JSON boolean is a valid schema. - var b bool - if err := json.Unmarshal(data, &b); err == nil { - if b { - // true is the empty schema, which validates everything. - *s = Schema{} - } else { - // false is the schema that validates nothing. - *s = *falseSchema() - } - return nil - } - - ms := struct { - Type json.RawMessage `json:"type,omitempty"` - Const json.RawMessage `json:"const,omitempty"` - MinLength *integer `json:"minLength,omitempty"` - MaxLength *integer `json:"maxLength,omitempty"` - MinItems *integer `json:"minItems,omitempty"` - MaxItems *integer `json:"maxItems,omitempty"` - MinProperties *integer `json:"minProperties,omitempty"` - MaxProperties *integer `json:"maxProperties,omitempty"` - MinContains *integer `json:"minContains,omitempty"` - MaxContains *integer `json:"maxContains,omitempty"` - - *schemaWithoutMethods - }{ - schemaWithoutMethods: (*schemaWithoutMethods)(s), - } - if err := unmarshalStructWithMap(data, &ms, "Extra"); err != nil { - return err - } - // Unmarshal "type" as either Type or Types. - var err error - if len(ms.Type) > 0 { - switch ms.Type[0] { - case '"': - err = json.Unmarshal(ms.Type, &s.Type) - case '[': - err = json.Unmarshal(ms.Type, &s.Types) - default: - err = fmt.Errorf(`invalid value for "type": %q`, ms.Type) - } - } - if err != nil { - return err - } - - unmarshalAnyPtr := func(p **any, raw json.RawMessage) error { - if len(raw) == 0 { - return nil - } - if bytes.Equal(raw, []byte("null")) { - *p = new(any) - return nil - } - return json.Unmarshal(raw, p) - } - - // Setting Const to a pointer to null will marshal properly, but won't - // unmarshal: the *any is set to nil, not a pointer to nil. - if err := unmarshalAnyPtr(&s.Const, ms.Const); err != nil { - return err - } - - set := func(dst **int, src *integer) { - if src != nil { - *dst = Ptr(int(*src)) - } - } - - set(&s.MinLength, ms.MinLength) - set(&s.MaxLength, ms.MaxLength) - set(&s.MinItems, ms.MinItems) - set(&s.MaxItems, ms.MaxItems) - set(&s.MinProperties, ms.MinProperties) - set(&s.MaxProperties, ms.MaxProperties) - set(&s.MinContains, ms.MinContains) - set(&s.MaxContains, ms.MaxContains) - - return nil -} - -type integer int32 // for the integer-valued fields of Schema - -func (ip *integer) UnmarshalJSON(data []byte) error { - if len(data) == 0 { - // nothing to do - return nil - } - // If there is a decimal point, src is a floating-point number. - var i int64 - if bytes.ContainsRune(data, '.') { - var f float64 - if err := json.Unmarshal(data, &f); err != nil { - return errors.New("not a number") - } - i = int64(f) - if float64(i) != f { - return errors.New("not an integer value") - } - } else { - if err := json.Unmarshal(data, &i); err != nil { - return errors.New("cannot be unmarshaled into an int") - } - } - // Ensure behavior is the same on both 32-bit and 64-bit systems. - if i < math.MinInt32 || i > math.MaxInt32 { - return errors.New("integer is out of range") - } - *ip = integer(i) - return nil -} - -// Ptr returns a pointer to a new variable whose value is x. -func Ptr[T any](x T) *T { return &x } - -// every applies f preorder to every schema under s including s. -// The second argument to f is the path to the schema appended to the argument path. -// It stops when f returns false. -func (s *Schema) every(f func(*Schema) bool) bool { - return f(s) && s.everyChild(func(s *Schema) bool { return s.every(f) }) -} - -// everyChild reports whether f is true for every immediate child schema of s. -func (s *Schema) everyChild(f func(*Schema) bool) bool { - v := reflect.ValueOf(s) - for _, info := range schemaFieldInfos { - fv := v.Elem().FieldByIndex(info.sf.Index) - switch info.sf.Type { - case schemaType: - // A field that contains an individual schema. A nil is valid: it just means the field isn't present. - c := fv.Interface().(*Schema) - if c != nil && !f(c) { - return false - } - - case schemaSliceType: - slice := fv.Interface().([]*Schema) - for _, c := range slice { - if !f(c) { - return false - } - } - - case schemaMapType: - // Sort keys for determinism. - m := fv.Interface().(map[string]*Schema) - for _, k := range slices.Sorted(maps.Keys(m)) { - if !f(m[k]) { - return false - } - } - } - } - return true -} - -// all wraps every in an iterator. -func (s *Schema) all() iter.Seq[*Schema] { - return func(yield func(*Schema) bool) { s.every(yield) } -} - -// children wraps everyChild in an iterator. -func (s *Schema) children() iter.Seq[*Schema] { - return func(yield func(*Schema) bool) { s.everyChild(yield) } -} - -var ( - schemaType = reflect.TypeFor[*Schema]() - schemaSliceType = reflect.TypeFor[[]*Schema]() - schemaMapType = reflect.TypeFor[map[string]*Schema]() -) - -type structFieldInfo struct { - sf reflect.StructField - jsonName string -} - -var ( - // the visible fields of Schema that have a JSON name, sorted by that name - schemaFieldInfos []structFieldInfo - // map from JSON name to field - schemaFieldMap = map[string]reflect.StructField{} -) - -func init() { - for _, sf := range reflect.VisibleFields(reflect.TypeFor[Schema]()) { - info := fieldJSONInfo(sf) - if !info.omit { - schemaFieldInfos = append(schemaFieldInfos, structFieldInfo{sf, info.name}) - } - } - slices.SortFunc(schemaFieldInfos, func(i1, i2 structFieldInfo) int { - return cmp.Compare(i1.jsonName, i2.jsonName) - }) - for _, info := range schemaFieldInfos { - schemaFieldMap[info.jsonName] = info.sf - } -} diff --git a/jsonschema/schema_test.go b/jsonschema/schema_test.go deleted file mode 100644 index 19f6c6c7..00000000 --- a/jsonschema/schema_test.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "encoding/json" - "fmt" - "math" - "regexp" - "testing" -) - -func TestGoRoundTrip(t *testing.T) { - // Verify that Go representations round-trip. - for _, s := range []*Schema{ - {Type: "null"}, - {Types: []string{"null", "number"}}, - {Type: "string", MinLength: Ptr(20)}, - {Minimum: Ptr(20.0)}, - {Items: &Schema{Type: "integer"}}, - {Const: Ptr(any(0))}, - {Const: Ptr(any(nil))}, - {Const: Ptr(any([]int{}))}, - {Const: Ptr(any(map[string]any{}))}, - {Default: mustMarshal(1)}, - {Default: mustMarshal(nil)}, - {Extra: map[string]any{"test": "value"}}, - } { - data, err := json.Marshal(s) - if err != nil { - t.Fatal(err) - } - var got *Schema - mustUnmarshal(t, data, &got) - if !Equal(got, s) { - t.Errorf("got %s, want %s", got.json(), s.json()) - if got.Const != nil && s.Const != nil { - t.Logf("Consts: got %#v (%[1]T), want %#v (%[2]T)", *got.Const, *s.Const) - } - } - } -} - -func TestJSONRoundTrip(t *testing.T) { - // Verify that JSON texts for schemas marshal into equivalent forms. - // We don't expect everything to round-trip perfectly. For example, "true" and "false" - // will turn into their object equivalents. - // But most things should. - // Some of these cases test Schema.{UnM,M}arshalJSON. - // Most of others follow from the behavior of encoding/json, but they are still - // valuable as regression tests of this package's behavior. - for _, tt := range []struct { - in, want string - }{ - {`true`, `true`}, - {`false`, `false`}, - {`{"type":"", "enum":null}`, `true`}, // empty fields are omitted - {`{"minimum":1}`, `{"minimum":1}`}, - {`{"minimum":1.0}`, `{"minimum":1}`}, // floating-point integers lose their fractional part - {`{"minLength":1.0}`, `{"minLength":1}`}, // some floats are unmarshaled into ints, but you can't tell - { - // map keys are sorted - `{"$vocabulary":{"b":true, "a":false}}`, - `{"$vocabulary":{"a":false,"b":true}}`, - }, - {`{"unk":0}`, `{"unk":0}`}, // unknown fields are not dropped - { - // known and unknown fields are not dropped - // note that the order will be by the declaration order in the anonymous struct inside MarshalJSON - `{"comment":"test","type":"example","unk":0}`, - `{"type":"example","comment":"test","unk":0}`, - }, - {`{"extra":0}`, `{"extra":0}`}, // extra is not a special keyword and should not be dropped - {`{"Extra":0}`, `{"Extra":0}`}, // Extra is not a special keyword and should not be dropped - } { - var s Schema - mustUnmarshal(t, []byte(tt.in), &s) - data, err := json.Marshal(&s) - if err != nil { - t.Fatal(err) - } - if got := string(data); got != tt.want { - t.Errorf("%s:\ngot %s\nwant %s", tt.in, got, tt.want) - } - } -} - -func TestUnmarshalErrors(t *testing.T) { - for _, tt := range []struct { - in string - want string // error must match this regexp - }{ - {`1`, "cannot unmarshal number"}, - {`{"type":1}`, `invalid value for "type"`}, - {`{"minLength":1.5}`, `not an integer value`}, - {`{"maxLength":1.5}`, `not an integer value`}, - {`{"minItems":1.5}`, `not an integer value`}, - {`{"maxItems":1.5}`, `not an integer value`}, - {`{"minProperties":1.5}`, `not an integer value`}, - {`{"maxProperties":1.5}`, `not an integer value`}, - {`{"minContains":1.5}`, `not an integer value`}, - {`{"maxContains":1.5}`, `not an integer value`}, - {fmt.Sprintf(`{"maxContains":%d}`, int64(math.MaxInt32+1)), `out of range`}, - {`{"minLength":9e99}`, `cannot be unmarshaled`}, - {`{"minLength":"1.5"}`, `not a number`}, - } { - var s Schema - err := json.Unmarshal([]byte(tt.in), &s) - if err == nil { - t.Fatalf("%s: no error but expected one", tt.in) - } - if !regexp.MustCompile(tt.want).MatchString(err.Error()) { - t.Errorf("%s: error %q does not match %q", tt.in, err, tt.want) - } - - } -} - -func mustUnmarshal(t *testing.T, data []byte, ptr any) { - t.Helper() - if err := json.Unmarshal(data, ptr); err != nil { - t.Fatal(err) - } -} - -// json returns the schema in json format. -func (s *Schema) json() string { - data, err := json.Marshal(s) - if err != nil { - return fmt.Sprintf("", err) - } - return string(data) -} - -// json returns the schema in json format, indented. -func (s *Schema) jsonIndent() string { - data, err := json.MarshalIndent(s, "", " ") - if err != nil { - return fmt.Sprintf("", err) - } - return string(data) -} - -func TestCloneSchemas(t *testing.T) { - ss1 := &Schema{Type: "string"} - ss2 := &Schema{Type: "integer"} - ss3 := &Schema{Type: "boolean"} - ss4 := &Schema{Type: "number"} - ss5 := &Schema{Contains: ss4} - - s1 := Schema{ - Contains: ss1, - PrefixItems: []*Schema{ss2, ss3}, - Properties: map[string]*Schema{"a": ss5}, - } - s2 := s1.CloneSchemas() - - // The clones should appear identical. - if g, w := s1.json(), s2.json(); g != w { - t.Errorf("\ngot %s\nwant %s", g, w) - } - // None of the schemas should overlap. - schemas1 := map[*Schema]bool{ss1: true, ss2: true, ss3: true, ss4: true, ss5: true} - for ss := range s2.all() { - if schemas1[ss] { - t.Errorf("uncloned schema %s", ss.json()) - } - } - // s1's original schemas should be intact. - if s1.Contains != ss1 || s1.PrefixItems[0] != ss2 || s1.PrefixItems[1] != ss3 || ss5.Contains != ss4 || s1.Properties["a"] != ss5 { - t.Errorf("s1 modified") - } -} diff --git a/jsonschema/testdata/draft2020-12/README.md b/jsonschema/testdata/draft2020-12/README.md deleted file mode 100644 index dbc397dd..00000000 --- a/jsonschema/testdata/draft2020-12/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# JSON Schema test suite for 2020-12 - -These files were copied from -https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/83e866b46c9f9e7082fd51e83a61c5f2145a1ab7/tests/draft2020-12. - -The following files were omitted: - -content.json: it is not required to validate content fields -(https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.8.1). - -format.json: it is not required to validate format fields (https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.7.1). - -vocabulary.json: this package doesn't support explicit vocabularies, other than the 2020-12 draft. - -The "optional" directory: this package doesn't implement any optional features. diff --git a/jsonschema/testdata/draft2020-12/additionalProperties.json b/jsonschema/testdata/draft2020-12/additionalProperties.json deleted file mode 100644 index 9618575e..00000000 --- a/jsonschema/testdata/draft2020-12/additionalProperties.json +++ /dev/null @@ -1,219 +0,0 @@ -[ - { - "description": - "additionalProperties being false does not allow other properties", - "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema. Boolean \"false\" forbids everything." } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"foo": {}, "bar": {}}, - "patternProperties": { "^v": {} }, - "additionalProperties": false - }, - "tests": [ - { - "description": "no additional properties is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "an additional property is invalid", - "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, - "valid": false - }, - { - "description": "ignores arrays", - "data": [1, 2, 3], - "valid": true - }, - { - "description": "ignores strings", - "data": "foobarbaz", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - }, - { - "description": "patternProperties are not additional properties", - "data": {"foo":1, "vroom": 2}, - "valid": true - } - ] - }, - { - "description": "non-ASCII pattern with additionalProperties", - "specification": [ { "core":"10.3.2.3"} ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": {"^รก": {}}, - "additionalProperties": false - }, - "tests": [ - { - "description": "matching the pattern is valid", - "data": {"รกrmรกnyos": 2}, - "valid": true - }, - { - "description": "not matching the pattern is invalid", - "data": {"รฉlmรฉny": 2}, - "valid": false - } - ] - }, - { - "description": "additionalProperties with schema", - "specification": [ { "core":"10.3.2.3", "quote": "The value of \"additionalProperties\" MUST be a valid JSON Schema." } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"foo": {}, "bar": {}}, - "additionalProperties": {"type": "boolean"} - }, - "tests": [ - { - "description": "no additional properties is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "an additional valid property is valid", - "data": {"foo" : 1, "bar" : 2, "quux" : true}, - "valid": true - }, - { - "description": "an additional invalid property is invalid", - "data": {"foo" : 1, "bar" : 2, "quux" : 12}, - "valid": false - } - ] - }, - { - "description": "additionalProperties can exist by itself", - "specification": [ { "core":"10.3.2.3", "quote": "With no other applicator applying to object instances. This validates all the instance values irrespective of their property names" } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "additionalProperties": {"type": "boolean"} - }, - "tests": [ - { - "description": "an additional valid property is valid", - "data": {"foo" : true}, - "valid": true - }, - { - "description": "an additional invalid property is invalid", - "data": {"foo" : 1}, - "valid": false - } - ] - }, - { - "description": "additionalProperties are allowed by default", - "specification": [ { "core":"10.3.2.3", "quote": "Omitting this keyword has the same assertion behavior as an empty schema." } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"foo": {}, "bar": {}} - }, - "tests": [ - { - "description": "additional properties are allowed", - "data": {"foo": 1, "bar": 2, "quux": true}, - "valid": true - } - ] - }, - { - "description": "additionalProperties does not look in applicators", - "specification":[ { "core": "10.2", "quote": "Subschemas of applicator keywords evaluate the instance completely independently such that the results of one such subschema MUST NOT impact the results of sibling subschemas." } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - {"properties": {"foo": {}}} - ], - "additionalProperties": {"type": "boolean"} - }, - "tests": [ - { - "description": "properties defined in allOf are not examined", - "data": {"foo": 1, "bar": true}, - "valid": false - } - ] - }, - { - "description": "additionalProperties with null valued instance properties", - "specification": [ { "core":"10.3.2.3" } ], - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "additionalProperties": { - "type": "null" - } - }, - "tests": [ - { - "description": "allows null values", - "data": {"foo": null}, - "valid": true - } - ] - }, - { - "description": "additionalProperties with propertyNames", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": { - "maxLength": 5 - }, - "additionalProperties": { - "type": "number" - } - }, - "tests": [ - { - "description": "Valid against both keywords", - "data": { "apple": 4 }, - "valid": true - }, - { - "description": "Valid against propertyNames, but not additionalProperties", - "data": { "fig": 2, "pear": "available" }, - "valid": false - } - ] - }, - { - "description": "dependentSchemas with additionalProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"foo2": {}}, - "dependentSchemas": { - "foo" : {}, - "foo2": { - "properties": { - "bar": {} - } - } - }, - "additionalProperties": false - }, - "tests": [ - { - "description": "additionalProperties doesn't consider dependentSchemas", - "data": {"foo": ""}, - "valid": false - }, - { - "description": "additionalProperties can't see bar", - "data": {"bar": ""}, - "valid": false - }, - { - "description": "additionalProperties can't see bar even when foo2 is present", - "data": {"foo2": "", "bar": ""}, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/allOf.json b/jsonschema/testdata/draft2020-12/allOf.json deleted file mode 100644 index 9e87903f..00000000 --- a/jsonschema/testdata/draft2020-12/allOf.json +++ /dev/null @@ -1,312 +0,0 @@ -[ - { - "description": "allOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "properties": { - "bar": {"type": "integer"} - }, - "required": ["bar"] - }, - { - "properties": { - "foo": {"type": "string"} - }, - "required": ["foo"] - } - ] - }, - "tests": [ - { - "description": "allOf", - "data": {"foo": "baz", "bar": 2}, - "valid": true - }, - { - "description": "mismatch second", - "data": {"foo": "baz"}, - "valid": false - }, - { - "description": "mismatch first", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "wrong type", - "data": {"foo": "baz", "bar": "quux"}, - "valid": false - } - ] - }, - { - "description": "allOf with base schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"bar": {"type": "integer"}}, - "required": ["bar"], - "allOf" : [ - { - "properties": { - "foo": {"type": "string"} - }, - "required": ["foo"] - }, - { - "properties": { - "baz": {"type": "null"} - }, - "required": ["baz"] - } - ] - }, - "tests": [ - { - "description": "valid", - "data": {"foo": "quux", "bar": 2, "baz": null}, - "valid": true - }, - { - "description": "mismatch base schema", - "data": {"foo": "quux", "baz": null}, - "valid": false - }, - { - "description": "mismatch first allOf", - "data": {"bar": 2, "baz": null}, - "valid": false - }, - { - "description": "mismatch second allOf", - "data": {"foo": "quux", "bar": 2}, - "valid": false - }, - { - "description": "mismatch both", - "data": {"bar": 2}, - "valid": false - } - ] - }, - { - "description": "allOf simple types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - {"maximum": 30}, - {"minimum": 20} - ] - }, - "tests": [ - { - "description": "valid", - "data": 25, - "valid": true - }, - { - "description": "mismatch one", - "data": 35, - "valid": false - } - ] - }, - { - "description": "allOf with boolean schemas, all true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [true, true] - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "allOf with boolean schemas, some false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [true, false] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "allOf with boolean schemas, all false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [false, false] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "allOf with one empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - {} - ] - }, - "tests": [ - { - "description": "any data is valid", - "data": 1, - "valid": true - } - ] - }, - { - "description": "allOf with two empty schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - {}, - {} - ] - }, - "tests": [ - { - "description": "any data is valid", - "data": 1, - "valid": true - } - ] - }, - { - "description": "allOf with the first empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - {}, - { "type": "number" } - ] - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "allOf with the last empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { "type": "number" }, - {} - ] - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "nested allOf, to check validation semantics", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "allOf": [ - { - "type": "null" - } - ] - } - ] - }, - "tests": [ - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "anything non-null is invalid", - "data": 123, - "valid": false - } - ] - }, - { - "description": "allOf combined with anyOf, oneOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ { "multipleOf": 2 } ], - "anyOf": [ { "multipleOf": 3 } ], - "oneOf": [ { "multipleOf": 5 } ] - }, - "tests": [ - { - "description": "allOf: false, anyOf: false, oneOf: false", - "data": 1, - "valid": false - }, - { - "description": "allOf: false, anyOf: false, oneOf: true", - "data": 5, - "valid": false - }, - { - "description": "allOf: false, anyOf: true, oneOf: false", - "data": 3, - "valid": false - }, - { - "description": "allOf: false, anyOf: true, oneOf: true", - "data": 15, - "valid": false - }, - { - "description": "allOf: true, anyOf: false, oneOf: false", - "data": 2, - "valid": false - }, - { - "description": "allOf: true, anyOf: false, oneOf: true", - "data": 10, - "valid": false - }, - { - "description": "allOf: true, anyOf: true, oneOf: false", - "data": 6, - "valid": false - }, - { - "description": "allOf: true, anyOf: true, oneOf: true", - "data": 30, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/anchor.json b/jsonschema/testdata/draft2020-12/anchor.json deleted file mode 100644 index 99143fa1..00000000 --- a/jsonschema/testdata/draft2020-12/anchor.json +++ /dev/null @@ -1,120 +0,0 @@ -[ - { - "description": "Location-independent identifier", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "#foo", - "$defs": { - "A": { - "$anchor": "foo", - "type": "integer" - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] - }, - { - "description": "Location-independent identifier with absolute URI", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/bar#foo", - "$defs": { - "A": { - "$id": "http://localhost:1234/draft2020-12/bar", - "$anchor": "foo", - "type": "integer" - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] - }, - { - "description": "Location-independent identifier with base URI change in subschema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/root", - "$ref": "http://localhost:1234/draft2020-12/nested.json#foo", - "$defs": { - "A": { - "$id": "nested.json", - "$defs": { - "B": { - "$anchor": "foo", - "type": "integer" - } - } - } - } - }, - "tests": [ - { - "data": 1, - "description": "match", - "valid": true - }, - { - "data": "a", - "description": "mismatch", - "valid": false - } - ] - }, - { - "description": "same $anchor with different base uri", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/foobar", - "$defs": { - "A": { - "$id": "child1", - "allOf": [ - { - "$id": "child2", - "$anchor": "my_anchor", - "type": "number" - }, - { - "$anchor": "my_anchor", - "type": "string" - } - ] - } - }, - "$ref": "child1#my_anchor" - }, - "tests": [ - { - "description": "$ref resolves to /$defs/A/allOf/1", - "data": "a", - "valid": true - }, - { - "description": "$ref does not resolve to /$defs/A/allOf/0", - "data": 1, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/anyOf.json b/jsonschema/testdata/draft2020-12/anyOf.json deleted file mode 100644 index 89b192db..00000000 --- a/jsonschema/testdata/draft2020-12/anyOf.json +++ /dev/null @@ -1,203 +0,0 @@ -[ - { - "description": "anyOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [ - { - "type": "integer" - }, - { - "minimum": 2 - } - ] - }, - "tests": [ - { - "description": "first anyOf valid", - "data": 1, - "valid": true - }, - { - "description": "second anyOf valid", - "data": 2.5, - "valid": true - }, - { - "description": "both anyOf valid", - "data": 3, - "valid": true - }, - { - "description": "neither anyOf valid", - "data": 1.5, - "valid": false - } - ] - }, - { - "description": "anyOf with base schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string", - "anyOf" : [ - { - "maxLength": 2 - }, - { - "minLength": 4 - } - ] - }, - "tests": [ - { - "description": "mismatch base schema", - "data": 3, - "valid": false - }, - { - "description": "one anyOf valid", - "data": "foobar", - "valid": true - }, - { - "description": "both anyOf invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "anyOf with boolean schemas, all true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [true, true] - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "anyOf with boolean schemas, some true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [true, false] - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "anyOf with boolean schemas, all false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [false, false] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "anyOf complex types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [ - { - "properties": { - "bar": {"type": "integer"} - }, - "required": ["bar"] - }, - { - "properties": { - "foo": {"type": "string"} - }, - "required": ["foo"] - } - ] - }, - "tests": [ - { - "description": "first anyOf valid (complex)", - "data": {"bar": 2}, - "valid": true - }, - { - "description": "second anyOf valid (complex)", - "data": {"foo": "baz"}, - "valid": true - }, - { - "description": "both anyOf valid (complex)", - "data": {"foo": "baz", "bar": 2}, - "valid": true - }, - { - "description": "neither anyOf valid (complex)", - "data": {"foo": 2, "bar": "quux"}, - "valid": false - } - ] - }, - { - "description": "anyOf with one empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [ - { "type": "number" }, - {} - ] - }, - "tests": [ - { - "description": "string is valid", - "data": "foo", - "valid": true - }, - { - "description": "number is valid", - "data": 123, - "valid": true - } - ] - }, - { - "description": "nested anyOf, to check validation semantics", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "anyOf": [ - { - "anyOf": [ - { - "type": "null" - } - ] - } - ] - }, - "tests": [ - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "anything non-null is invalid", - "data": 123, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/boolean_schema.json b/jsonschema/testdata/draft2020-12/boolean_schema.json deleted file mode 100644 index 6d40f23f..00000000 --- a/jsonschema/testdata/draft2020-12/boolean_schema.json +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "description": "boolean schema 'true'", - "schema": true, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "string is valid", - "data": "foo", - "valid": true - }, - { - "description": "boolean true is valid", - "data": true, - "valid": true - }, - { - "description": "boolean false is valid", - "data": false, - "valid": true - }, - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "object is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - }, - { - "description": "array is valid", - "data": ["foo"], - "valid": true - }, - { - "description": "empty array is valid", - "data": [], - "valid": true - } - ] - }, - { - "description": "boolean schema 'false'", - "schema": false, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - }, - { - "description": "boolean true is invalid", - "data": true, - "valid": false - }, - { - "description": "boolean false is invalid", - "data": false, - "valid": false - }, - { - "description": "null is invalid", - "data": null, - "valid": false - }, - { - "description": "object is invalid", - "data": {"foo": "bar"}, - "valid": false - }, - { - "description": "empty object is invalid", - "data": {}, - "valid": false - }, - { - "description": "array is invalid", - "data": ["foo"], - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/const.json b/jsonschema/testdata/draft2020-12/const.json deleted file mode 100644 index 50be86a0..00000000 --- a/jsonschema/testdata/draft2020-12/const.json +++ /dev/null @@ -1,387 +0,0 @@ -[ - { - "description": "const validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": 2 - }, - "tests": [ - { - "description": "same value is valid", - "data": 2, - "valid": true - }, - { - "description": "another value is invalid", - "data": 5, - "valid": false - }, - { - "description": "another type is invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "const with object", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": {"foo": "bar", "baz": "bax"} - }, - "tests": [ - { - "description": "same object is valid", - "data": {"foo": "bar", "baz": "bax"}, - "valid": true - }, - { - "description": "same object with different property order is valid", - "data": {"baz": "bax", "foo": "bar"}, - "valid": true - }, - { - "description": "another object is invalid", - "data": {"foo": "bar"}, - "valid": false - }, - { - "description": "another type is invalid", - "data": [1, 2], - "valid": false - } - ] - }, - { - "description": "const with array", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": [{ "foo": "bar" }] - }, - "tests": [ - { - "description": "same array is valid", - "data": [{"foo": "bar"}], - "valid": true - }, - { - "description": "another array item is invalid", - "data": [2], - "valid": false - }, - { - "description": "array with additional items is invalid", - "data": [1, 2, 3], - "valid": false - } - ] - }, - { - "description": "const with null", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": null - }, - "tests": [ - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "not null is invalid", - "data": 0, - "valid": false - } - ] - }, - { - "description": "const with false does not match 0", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": false - }, - "tests": [ - { - "description": "false is valid", - "data": false, - "valid": true - }, - { - "description": "integer zero is invalid", - "data": 0, - "valid": false - }, - { - "description": "float zero is invalid", - "data": 0.0, - "valid": false - } - ] - }, - { - "description": "const with true does not match 1", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": true - }, - "tests": [ - { - "description": "true is valid", - "data": true, - "valid": true - }, - { - "description": "integer one is invalid", - "data": 1, - "valid": false - }, - { - "description": "float one is invalid", - "data": 1.0, - "valid": false - } - ] - }, - { - "description": "const with [false] does not match [0]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": [false] - }, - "tests": [ - { - "description": "[false] is valid", - "data": [false], - "valid": true - }, - { - "description": "[0] is invalid", - "data": [0], - "valid": false - }, - { - "description": "[0.0] is invalid", - "data": [0.0], - "valid": false - } - ] - }, - { - "description": "const with [true] does not match [1]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": [true] - }, - "tests": [ - { - "description": "[true] is valid", - "data": [true], - "valid": true - }, - { - "description": "[1] is invalid", - "data": [1], - "valid": false - }, - { - "description": "[1.0] is invalid", - "data": [1.0], - "valid": false - } - ] - }, - { - "description": "const with {\"a\": false} does not match {\"a\": 0}", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": {"a": false} - }, - "tests": [ - { - "description": "{\"a\": false} is valid", - "data": {"a": false}, - "valid": true - }, - { - "description": "{\"a\": 0} is invalid", - "data": {"a": 0}, - "valid": false - }, - { - "description": "{\"a\": 0.0} is invalid", - "data": {"a": 0.0}, - "valid": false - } - ] - }, - { - "description": "const with {\"a\": true} does not match {\"a\": 1}", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": {"a": true} - }, - "tests": [ - { - "description": "{\"a\": true} is valid", - "data": {"a": true}, - "valid": true - }, - { - "description": "{\"a\": 1} is invalid", - "data": {"a": 1}, - "valid": false - }, - { - "description": "{\"a\": 1.0} is invalid", - "data": {"a": 1.0}, - "valid": false - } - ] - }, - { - "description": "const with 0 does not match other zero-like types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": 0 - }, - "tests": [ - { - "description": "false is invalid", - "data": false, - "valid": false - }, - { - "description": "integer zero is valid", - "data": 0, - "valid": true - }, - { - "description": "float zero is valid", - "data": 0.0, - "valid": true - }, - { - "description": "empty object is invalid", - "data": {}, - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - }, - { - "description": "empty string is invalid", - "data": "", - "valid": false - } - ] - }, - { - "description": "const with 1 does not match true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": 1 - }, - "tests": [ - { - "description": "true is invalid", - "data": true, - "valid": false - }, - { - "description": "integer one is valid", - "data": 1, - "valid": true - }, - { - "description": "float one is valid", - "data": 1.0, - "valid": true - } - ] - }, - { - "description": "const with -2.0 matches integer and float types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": -2.0 - }, - "tests": [ - { - "description": "integer -2 is valid", - "data": -2, - "valid": true - }, - { - "description": "integer 2 is invalid", - "data": 2, - "valid": false - }, - { - "description": "float -2.0 is valid", - "data": -2.0, - "valid": true - }, - { - "description": "float 2.0 is invalid", - "data": 2.0, - "valid": false - }, - { - "description": "float -2.00001 is invalid", - "data": -2.00001, - "valid": false - } - ] - }, - { - "description": "float and integers are equal up to 64-bit representation limits", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": 9007199254740992 - }, - "tests": [ - { - "description": "integer is valid", - "data": 9007199254740992, - "valid": true - }, - { - "description": "integer minus one is invalid", - "data": 9007199254740991, - "valid": false - }, - { - "description": "float is valid", - "data": 9007199254740992.0, - "valid": true - }, - { - "description": "float minus one is invalid", - "data": 9007199254740991.0, - "valid": false - } - ] - }, - { - "description": "nul characters in strings", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "const": "hello\u0000there" - }, - "tests": [ - { - "description": "match string with nul", - "data": "hello\u0000there", - "valid": true - }, - { - "description": "do not match string lacking nul", - "data": "hellothere", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/contains.json b/jsonschema/testdata/draft2020-12/contains.json deleted file mode 100644 index 08a00a75..00000000 --- a/jsonschema/testdata/draft2020-12/contains.json +++ /dev/null @@ -1,176 +0,0 @@ -[ - { - "description": "contains keyword validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"minimum": 5} - }, - "tests": [ - { - "description": "array with item matching schema (5) is valid", - "data": [3, 4, 5], - "valid": true - }, - { - "description": "array with item matching schema (6) is valid", - "data": [3, 4, 6], - "valid": true - }, - { - "description": "array with two items matching schema (5, 6) is valid", - "data": [3, 4, 5, 6], - "valid": true - }, - { - "description": "array without items matching schema is invalid", - "data": [2, 3, 4], - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - }, - { - "description": "not array is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "contains keyword with const keyword", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": { "const": 5 } - }, - "tests": [ - { - "description": "array with item 5 is valid", - "data": [3, 4, 5], - "valid": true - }, - { - "description": "array with two items 5 is valid", - "data": [3, 4, 5, 5], - "valid": true - }, - { - "description": "array without item 5 is invalid", - "data": [1, 2, 3, 4], - "valid": false - } - ] - }, - { - "description": "contains keyword with boolean schema true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": true - }, - "tests": [ - { - "description": "any non-empty array is valid", - "data": ["foo"], - "valid": true - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - } - ] - }, - { - "description": "contains keyword with boolean schema false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": false - }, - "tests": [ - { - "description": "any non-empty array is invalid", - "data": ["foo"], - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - }, - { - "description": "non-arrays are valid", - "data": "contains does not apply to strings", - "valid": true - } - ] - }, - { - "description": "items + contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": { "multipleOf": 2 }, - "contains": { "multipleOf": 3 } - }, - "tests": [ - { - "description": "matches items, does not match contains", - "data": [ 2, 4, 8 ], - "valid": false - }, - { - "description": "does not match items, matches contains", - "data": [ 3, 6, 9 ], - "valid": false - }, - { - "description": "matches both items and contains", - "data": [ 6, 12 ], - "valid": true - }, - { - "description": "matches neither items nor contains", - "data": [ 1, 5 ], - "valid": false - } - ] - }, - { - "description": "contains with false if subschema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": { - "if": false, - "else": true - } - }, - "tests": [ - { - "description": "any non-empty array is valid", - "data": ["foo"], - "valid": true - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - } - ] - }, - { - "description": "contains with null instance elements", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": { - "type": "null" - } - }, - "tests": [ - { - "description": "allows null items", - "data": [ null ], - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/default.json b/jsonschema/testdata/draft2020-12/default.json deleted file mode 100644 index ceb3ae27..00000000 --- a/jsonschema/testdata/draft2020-12/default.json +++ /dev/null @@ -1,82 +0,0 @@ -[ - { - "description": "invalid type for default", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": { - "type": "integer", - "default": [] - } - } - }, - "tests": [ - { - "description": "valid when property is specified", - "data": {"foo": 13}, - "valid": true - }, - { - "description": "still valid when the invalid default is used", - "data": {}, - "valid": true - } - ] - }, - { - "description": "invalid string value for default", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "bar": { - "type": "string", - "minLength": 4, - "default": "bad" - } - } - }, - "tests": [ - { - "description": "valid when property is specified", - "data": {"bar": "good"}, - "valid": true - }, - { - "description": "still valid when the invalid default is used", - "data": {}, - "valid": true - } - ] - }, - { - "description": "the default keyword does not do anything if the property is missing", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "alpha": { - "type": "number", - "maximum": 3, - "default": 5 - } - } - }, - "tests": [ - { - "description": "an explicit property value is checked against maximum (passing)", - "data": { "alpha": 1 }, - "valid": true - }, - { - "description": "an explicit property value is checked against maximum (failing)", - "data": { "alpha": 5 }, - "valid": false - }, - { - "description": "missing properties are not filled in with the default", - "data": {}, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/defs.json b/jsonschema/testdata/draft2020-12/defs.json deleted file mode 100644 index da2a503b..00000000 --- a/jsonschema/testdata/draft2020-12/defs.json +++ /dev/null @@ -1,21 +0,0 @@ -[ - { - "description": "validate definition against metaschema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "valid definition schema", - "data": {"$defs": {"foo": {"type": "integer"}}}, - "valid": true - }, - { - "description": "invalid definition schema", - "data": {"$defs": {"foo": {"type": 1}}}, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/dependentRequired.json b/jsonschema/testdata/draft2020-12/dependentRequired.json deleted file mode 100644 index 2baa38e9..00000000 --- a/jsonschema/testdata/draft2020-12/dependentRequired.json +++ /dev/null @@ -1,152 +0,0 @@ -[ - { - "description": "single dependency", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentRequired": {"bar": ["foo"]} - }, - "tests": [ - { - "description": "neither", - "data": {}, - "valid": true - }, - { - "description": "nondependant", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "with dependency", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "missing dependency", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "ignores arrays", - "data": ["bar"], - "valid": true - }, - { - "description": "ignores strings", - "data": "foobar", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "empty dependents", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentRequired": {"bar": []} - }, - "tests": [ - { - "description": "empty object", - "data": {}, - "valid": true - }, - { - "description": "object with one property", - "data": {"bar": 2}, - "valid": true - }, - { - "description": "non-object is valid", - "data": 1, - "valid": true - } - ] - }, - { - "description": "multiple dependents required", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentRequired": {"quux": ["foo", "bar"]} - }, - "tests": [ - { - "description": "neither", - "data": {}, - "valid": true - }, - { - "description": "nondependants", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "with dependencies", - "data": {"foo": 1, "bar": 2, "quux": 3}, - "valid": true - }, - { - "description": "missing dependency", - "data": {"foo": 1, "quux": 2}, - "valid": false - }, - { - "description": "missing other dependency", - "data": {"bar": 1, "quux": 2}, - "valid": false - }, - { - "description": "missing both dependencies", - "data": {"quux": 1}, - "valid": false - } - ] - }, - { - "description": "dependencies with escaped characters", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentRequired": { - "foo\nbar": ["foo\rbar"], - "foo\"bar": ["foo'bar"] - } - }, - "tests": [ - { - "description": "CRLF", - "data": { - "foo\nbar": 1, - "foo\rbar": 2 - }, - "valid": true - }, - { - "description": "quoted quotes", - "data": { - "foo'bar": 1, - "foo\"bar": 2 - }, - "valid": true - }, - { - "description": "CRLF missing dependent", - "data": { - "foo\nbar": 1, - "foo": 2 - }, - "valid": false - }, - { - "description": "quoted quotes missing dependent", - "data": { - "foo\"bar": 2 - }, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/dependentSchemas.json b/jsonschema/testdata/draft2020-12/dependentSchemas.json deleted file mode 100644 index 1c5f0574..00000000 --- a/jsonschema/testdata/draft2020-12/dependentSchemas.json +++ /dev/null @@ -1,171 +0,0 @@ -[ - { - "description": "single dependency", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentSchemas": { - "bar": { - "properties": { - "foo": {"type": "integer"}, - "bar": {"type": "integer"} - } - } - } - }, - "tests": [ - { - "description": "valid", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "no dependency", - "data": {"foo": "quux"}, - "valid": true - }, - { - "description": "wrong type", - "data": {"foo": "quux", "bar": 2}, - "valid": false - }, - { - "description": "wrong type other", - "data": {"foo": 2, "bar": "quux"}, - "valid": false - }, - { - "description": "wrong type both", - "data": {"foo": "quux", "bar": "quux"}, - "valid": false - }, - { - "description": "ignores arrays", - "data": ["bar"], - "valid": true - }, - { - "description": "ignores strings", - "data": "foobar", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "boolean subschemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentSchemas": { - "foo": true, - "bar": false - } - }, - "tests": [ - { - "description": "object with property having schema true is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "object with property having schema false is invalid", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "object with both properties is invalid", - "data": {"foo": 1, "bar": 2}, - "valid": false - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "dependencies with escaped characters", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "dependentSchemas": { - "foo\tbar": {"minProperties": 4}, - "foo'bar": {"required": ["foo\"bar"]} - } - }, - "tests": [ - { - "description": "quoted tab", - "data": { - "foo\tbar": 1, - "a": 2, - "b": 3, - "c": 4 - }, - "valid": true - }, - { - "description": "quoted quote", - "data": { - "foo'bar": {"foo\"bar": 1} - }, - "valid": false - }, - { - "description": "quoted tab invalid under dependent schema", - "data": { - "foo\tbar": 1, - "a": 2 - }, - "valid": false - }, - { - "description": "quoted quote invalid under dependent schema", - "data": {"foo'bar": 1}, - "valid": false - } - ] - }, - { - "description": "dependent subschema incompatible with root", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {} - }, - "dependentSchemas": { - "foo": { - "properties": { - "bar": {} - }, - "additionalProperties": false - } - } - }, - "tests": [ - { - "description": "matches root", - "data": {"foo": 1}, - "valid": false - }, - { - "description": "matches dependency", - "data": {"bar": 1}, - "valid": true - }, - { - "description": "matches both", - "data": {"foo": 1, "bar": 2}, - "valid": false - }, - { - "description": "no dependency", - "data": {"baz": 1}, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/dynamicRef.json b/jsonschema/testdata/draft2020-12/dynamicRef.json deleted file mode 100644 index ffa211ba..00000000 --- a/jsonschema/testdata/draft2020-12/dynamicRef.json +++ /dev/null @@ -1,815 +0,0 @@ -[ - { - "description": "A $dynamicRef to a $dynamicAnchor in the same schema resource behaves like a normal $ref to an $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamicRef-dynamicAnchor-same-schema/root", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - } - } - }, - "tests": [ - { - "description": "An array of strings is valid", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "An array containing non-strings is invalid", - "data": ["foo", 42], - "valid": false - } - ] - }, - { - "description": "A $dynamicRef to an $anchor in the same schema resource behaves like a normal $ref to an $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamicRef-anchor-same-schema/root", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "foo": { - "$anchor": "items", - "type": "string" - } - } - }, - "tests": [ - { - "description": "An array of strings is valid", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "An array containing non-strings is invalid", - "data": ["foo", 42], - "valid": false - } - ] - }, - { - "description": "A $ref to a $dynamicAnchor in the same schema resource behaves like a normal $ref to an $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/ref-dynamicAnchor-same-schema/root", - "type": "array", - "items": { "$ref": "#items" }, - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - } - } - }, - "tests": [ - { - "description": "An array of strings is valid", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "An array containing non-strings is invalid", - "data": ["foo", 42], - "valid": false - } - ] - }, - { - "description": "A $dynamicRef resolves to the first $dynamicAnchor still in scope that is encountered when the schema is evaluated", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/typical-dynamic-resolution/root", - "$ref": "list", - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "items": { - "$comment": "This is only needed to satisfy the bookending requirement", - "$dynamicAnchor": "items" - } - } - } - } - }, - "tests": [ - { - "description": "An array of strings is valid", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "An array containing non-strings is invalid", - "data": ["foo", 42], - "valid": false - } - ] - }, - { - "description": "A $dynamicRef without anchor in fragment behaves identical to $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamicRef-without-anchor/root", - "$ref": "list", - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#/$defs/items" }, - "$defs": { - "items": { - "$comment": "This is only needed to satisfy the bookending requirement", - "$dynamicAnchor": "items", - "type": "number" - } - } - } - } - }, - "tests": [ - { - "description": "An array of strings is invalid", - "data": ["foo", "bar"], - "valid": false - }, - { - "description": "An array of numbers is valid", - "data": [24, 42], - "valid": true - } - ] - }, - { - "description": "A $dynamicRef with intermediate scopes that don't include a matching $dynamicAnchor does not affect dynamic scope resolution", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-resolution-with-intermediate-scopes/root", - "$ref": "intermediate-scope", - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - }, - "intermediate-scope": { - "$id": "intermediate-scope", - "$ref": "list" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "items": { - "$comment": "This is only needed to satisfy the bookending requirement", - "$dynamicAnchor": "items" - } - } - } - } - }, - "tests": [ - { - "description": "An array of strings is valid", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "An array containing non-strings is invalid", - "data": ["foo", 42], - "valid": false - } - ] - }, - { - "description": "An $anchor with the same name as a $dynamicAnchor is not used for dynamic scope resolution", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-resolution-ignores-anchors/root", - "$ref": "list", - "$defs": { - "foo": { - "$anchor": "items", - "type": "string" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "items": { - "$comment": "This is only needed to satisfy the bookending requirement", - "$dynamicAnchor": "items" - } - } - } - } - }, - "tests": [ - { - "description": "Any array is valid", - "data": ["foo", 42], - "valid": true - } - ] - }, - { - "description": "A $dynamicRef without a matching $dynamicAnchor in the same schema resource behaves like a normal $ref to $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-resolution-without-bookend/root", - "$ref": "list", - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "items": { - "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref", - "$anchor": "items" - } - } - } - } - }, - "tests": [ - { - "description": "Any array is valid", - "data": ["foo", 42], - "valid": true - } - ] - }, - { - "description": "A $dynamicRef with a non-matching $dynamicAnchor in the same schema resource behaves like a normal $ref to $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/unmatched-dynamic-anchor/root", - "$ref": "list", - "$defs": { - "foo": { - "$dynamicAnchor": "items", - "type": "string" - }, - "list": { - "$id": "list", - "type": "array", - "items": { "$dynamicRef": "#items" }, - "$defs": { - "items": { - "$comment": "This is only needed to give the reference somewhere to resolve to when it behaves like $ref", - "$anchor": "items", - "$dynamicAnchor": "foo" - } - } - } - } - }, - "tests": [ - { - "description": "Any array is valid", - "data": ["foo", 42], - "valid": true - } - ] - }, - { - "description": "A $dynamicRef that initially resolves to a schema with a matching $dynamicAnchor resolves to the first $dynamicAnchor in the dynamic scope", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/relative-dynamic-reference/root", - "$dynamicAnchor": "meta", - "type": "object", - "properties": { - "foo": { "const": "pass" } - }, - "$ref": "extended", - "$defs": { - "extended": { - "$id": "extended", - "$dynamicAnchor": "meta", - "type": "object", - "properties": { - "bar": { "$ref": "bar" } - } - }, - "bar": { - "$id": "bar", - "type": "object", - "properties": { - "baz": { "$dynamicRef": "extended#meta" } - } - } - } - }, - "tests": [ - { - "description": "The recursive part is valid against the root", - "data": { - "foo": "pass", - "bar": { - "baz": { "foo": "pass" } - } - }, - "valid": true - }, - { - "description": "The recursive part is not valid against the root", - "data": { - "foo": "pass", - "bar": { - "baz": { "foo": "fail" } - } - }, - "valid": false - } - ] - }, - { - "description": "A $dynamicRef that initially resolves to a schema without a matching $dynamicAnchor behaves like a normal $ref to $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/relative-dynamic-reference-without-bookend/root", - "$dynamicAnchor": "meta", - "type": "object", - "properties": { - "foo": { "const": "pass" } - }, - "$ref": "extended", - "$defs": { - "extended": { - "$id": "extended", - "$anchor": "meta", - "type": "object", - "properties": { - "bar": { "$ref": "bar" } - } - }, - "bar": { - "$id": "bar", - "type": "object", - "properties": { - "baz": { "$dynamicRef": "extended#meta" } - } - } - } - }, - "tests": [ - { - "description": "The recursive part doesn't need to validate against the root", - "data": { - "foo": "pass", - "bar": { - "baz": { "foo": "fail" } - } - }, - "valid": true - } - ] - }, - { - "description": "multiple dynamic paths to the $dynamicRef keyword", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-ref-with-multiple-paths/main", - "if": { - "properties": { - "kindOfList": { "const": "numbers" } - }, - "required": ["kindOfList"] - }, - "then": { "$ref": "numberList" }, - "else": { "$ref": "stringList" }, - - "$defs": { - "genericList": { - "$id": "genericList", - "properties": { - "list": { - "items": { "$dynamicRef": "#itemType" } - } - }, - "$defs": { - "defaultItemType": { - "$comment": "Only needed to satisfy bookending requirement", - "$dynamicAnchor": "itemType" - } - } - }, - "numberList": { - "$id": "numberList", - "$defs": { - "itemType": { - "$dynamicAnchor": "itemType", - "type": "number" - } - }, - "$ref": "genericList" - }, - "stringList": { - "$id": "stringList", - "$defs": { - "itemType": { - "$dynamicAnchor": "itemType", - "type": "string" - } - }, - "$ref": "genericList" - } - } - }, - "tests": [ - { - "description": "number list with number values", - "data": { - "kindOfList": "numbers", - "list": [1.1] - }, - "valid": true - }, - { - "description": "number list with string values", - "data": { - "kindOfList": "numbers", - "list": ["foo"] - }, - "valid": false - }, - { - "description": "string list with number values", - "data": { - "kindOfList": "strings", - "list": [1.1] - }, - "valid": false - }, - { - "description": "string list with string values", - "data": { - "kindOfList": "strings", - "list": ["foo"] - }, - "valid": true - } - ] - }, - { - "description": "after leaving a dynamic scope, it is not used by a $dynamicRef", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-ref-leaving-dynamic-scope/main", - "if": { - "$id": "first_scope", - "$defs": { - "thingy": { - "$comment": "this is first_scope#thingy", - "$dynamicAnchor": "thingy", - "type": "number" - } - } - }, - "then": { - "$id": "second_scope", - "$ref": "start", - "$defs": { - "thingy": { - "$comment": "this is second_scope#thingy, the final destination of the $dynamicRef", - "$dynamicAnchor": "thingy", - "type": "null" - } - } - }, - "$defs": { - "start": { - "$comment": "this is the landing spot from $ref", - "$id": "start", - "$dynamicRef": "inner_scope#thingy" - }, - "thingy": { - "$comment": "this is the first stop for the $dynamicRef", - "$id": "inner_scope", - "$dynamicAnchor": "thingy", - "type": "string" - } - } - }, - "tests": [ - { - "description": "string matches /$defs/thingy, but the $dynamicRef does not stop here", - "data": "a string", - "valid": false - }, - { - "description": "first_scope is not in dynamic scope for the $dynamicRef", - "data": 42, - "valid": false - }, - { - "description": "/then/$defs/thingy is the final stop for the $dynamicRef", - "data": null, - "valid": true - } - ] - }, - { - "description": "strict-tree schema, guards against misspelled properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/strict-tree.json", - "$dynamicAnchor": "node", - - "$ref": "tree.json", - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "instance with misspelled field", - "data": { - "children": [{ - "daat": 1 - }] - }, - "valid": false - }, - { - "description": "instance with correct field", - "data": { - "children": [{ - "data": 1 - }] - }, - "valid": true - } - ] - }, - { - "description": "tests for implementation dynamic anchor and reference link", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/strict-extendible.json", - "$ref": "extendible-dynamic-ref.json", - "$defs": { - "elements": { - "$dynamicAnchor": "elements", - "properties": { - "a": true - }, - "required": ["a"], - "additionalProperties": false - } - } - }, - "tests": [ - { - "description": "incorrect parent schema", - "data": { - "a": true - }, - "valid": false - }, - { - "description": "incorrect extended schema", - "data": { - "elements": [ - { "b": 1 } - ] - }, - "valid": false - }, - { - "description": "correct extended schema", - "data": { - "elements": [ - { "a": 1 } - ] - }, - "valid": true - } - ] - }, - { - "description": "$ref and $dynamicAnchor are independent of order - $defs first", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/strict-extendible-allof-defs-first.json", - "allOf": [ - { - "$ref": "extendible-dynamic-ref.json" - }, - { - "$defs": { - "elements": { - "$dynamicAnchor": "elements", - "properties": { - "a": true - }, - "required": ["a"], - "additionalProperties": false - } - } - } - ] - }, - "tests": [ - { - "description": "incorrect parent schema", - "data": { - "a": true - }, - "valid": false - }, - { - "description": "incorrect extended schema", - "data": { - "elements": [ - { "b": 1 } - ] - }, - "valid": false - }, - { - "description": "correct extended schema", - "data": { - "elements": [ - { "a": 1 } - ] - }, - "valid": true - } - ] - }, - { - "description": "$ref and $dynamicAnchor are independent of order - $ref first", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/strict-extendible-allof-ref-first.json", - "allOf": [ - { - "$defs": { - "elements": { - "$dynamicAnchor": "elements", - "properties": { - "a": true - }, - "required": ["a"], - "additionalProperties": false - } - } - }, - { - "$ref": "extendible-dynamic-ref.json" - } - ] - }, - "tests": [ - { - "description": "incorrect parent schema", - "data": { - "a": true - }, - "valid": false - }, - { - "description": "incorrect extended schema", - "data": { - "elements": [ - { "b": 1 } - ] - }, - "valid": false - }, - { - "description": "correct extended schema", - "data": { - "elements": [ - { "a": 1 } - ] - }, - "valid": true - } - ] - }, - { - "description": "$ref to $dynamicRef finds detached $dynamicAnchor", - "schema": { - "$ref": "http://localhost:1234/draft2020-12/detached-dynamicref.json#/$defs/foo" - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "$dynamicRef points to a boolean schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "true": true, - "false": false - }, - "properties": { - "true": { - "$dynamicRef": "#/$defs/true" - }, - "false": { - "$dynamicRef": "#/$defs/false" - } - } - }, - "tests": [ - { - "description": "follow $dynamicRef to a true schema", - "data": { "true": 1 }, - "valid": true - }, - { - "description": "follow $dynamicRef to a false schema", - "data": { "false": 1 }, - "valid": false - } - ] - }, - { - "description": "$dynamicRef skips over intermediate resources - direct reference", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://test.json-schema.org/dynamic-ref-skips-intermediate-resource/main", - "type": "object", - "properties": { - "bar-item": { - "$ref": "item" - } - }, - "$defs": { - "bar": { - "$id": "bar", - "type": "array", - "items": { - "$ref": "item" - }, - "$defs": { - "item": { - "$id": "item", - "type": "object", - "properties": { - "content": { - "$dynamicRef": "#content" - } - }, - "$defs": { - "defaultContent": { - "$dynamicAnchor": "content", - "type": "integer" - } - } - }, - "content": { - "$dynamicAnchor": "content", - "type": "string" - } - } - } - } - }, - "tests": [ - { - "description": "integer property passes", - "data": { "bar-item": { "content": 42 } }, - "valid": true - }, - { - "description": "string property fails", - "data": { "bar-item": { "content": "value" } }, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/enum.json b/jsonschema/testdata/draft2020-12/enum.json deleted file mode 100644 index c8f35eac..00000000 --- a/jsonschema/testdata/draft2020-12/enum.json +++ /dev/null @@ -1,358 +0,0 @@ -[ - { - "description": "simple enum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [1, 2, 3] - }, - "tests": [ - { - "description": "one of the enum is valid", - "data": 1, - "valid": true - }, - { - "description": "something else is invalid", - "data": 4, - "valid": false - } - ] - }, - { - "description": "heterogeneous enum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [6, "foo", [], true, {"foo": 12}] - }, - "tests": [ - { - "description": "one of the enum is valid", - "data": [], - "valid": true - }, - { - "description": "something else is invalid", - "data": null, - "valid": false - }, - { - "description": "objects are deep compared", - "data": {"foo": false}, - "valid": false - }, - { - "description": "valid object matches", - "data": {"foo": 12}, - "valid": true - }, - { - "description": "extra properties in object is invalid", - "data": {"foo": 12, "boo": 42}, - "valid": false - } - ] - }, - { - "description": "heterogeneous enum-with-null validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [6, null] - }, - "tests": [ - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "number is valid", - "data": 6, - "valid": true - }, - { - "description": "something else is invalid", - "data": "test", - "valid": false - } - ] - }, - { - "description": "enums in properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type":"object", - "properties": { - "foo": {"enum":["foo"]}, - "bar": {"enum":["bar"]} - }, - "required": ["bar"] - }, - "tests": [ - { - "description": "both properties are valid", - "data": {"foo":"foo", "bar":"bar"}, - "valid": true - }, - { - "description": "wrong foo value", - "data": {"foo":"foot", "bar":"bar"}, - "valid": false - }, - { - "description": "wrong bar value", - "data": {"foo":"foo", "bar":"bart"}, - "valid": false - }, - { - "description": "missing optional property is valid", - "data": {"bar":"bar"}, - "valid": true - }, - { - "description": "missing required property is invalid", - "data": {"foo":"foo"}, - "valid": false - }, - { - "description": "missing all properties is invalid", - "data": {}, - "valid": false - } - ] - }, - { - "description": "enum with escaped characters", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": ["foo\nbar", "foo\rbar"] - }, - "tests": [ - { - "description": "member 1 is valid", - "data": "foo\nbar", - "valid": true - }, - { - "description": "member 2 is valid", - "data": "foo\rbar", - "valid": true - }, - { - "description": "another string is invalid", - "data": "abc", - "valid": false - } - ] - }, - { - "description": "enum with false does not match 0", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [false] - }, - "tests": [ - { - "description": "false is valid", - "data": false, - "valid": true - }, - { - "description": "integer zero is invalid", - "data": 0, - "valid": false - }, - { - "description": "float zero is invalid", - "data": 0.0, - "valid": false - } - ] - }, - { - "description": "enum with [false] does not match [0]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [[false]] - }, - "tests": [ - { - "description": "[false] is valid", - "data": [false], - "valid": true - }, - { - "description": "[0] is invalid", - "data": [0], - "valid": false - }, - { - "description": "[0.0] is invalid", - "data": [0.0], - "valid": false - } - ] - }, - { - "description": "enum with true does not match 1", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [true] - }, - "tests": [ - { - "description": "true is valid", - "data": true, - "valid": true - }, - { - "description": "integer one is invalid", - "data": 1, - "valid": false - }, - { - "description": "float one is invalid", - "data": 1.0, - "valid": false - } - ] - }, - { - "description": "enum with [true] does not match [1]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [[true]] - }, - "tests": [ - { - "description": "[true] is valid", - "data": [true], - "valid": true - }, - { - "description": "[1] is invalid", - "data": [1], - "valid": false - }, - { - "description": "[1.0] is invalid", - "data": [1.0], - "valid": false - } - ] - }, - { - "description": "enum with 0 does not match false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [0] - }, - "tests": [ - { - "description": "false is invalid", - "data": false, - "valid": false - }, - { - "description": "integer zero is valid", - "data": 0, - "valid": true - }, - { - "description": "float zero is valid", - "data": 0.0, - "valid": true - } - ] - }, - { - "description": "enum with [0] does not match [false]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [[0]] - }, - "tests": [ - { - "description": "[false] is invalid", - "data": [false], - "valid": false - }, - { - "description": "[0] is valid", - "data": [0], - "valid": true - }, - { - "description": "[0.0] is valid", - "data": [0.0], - "valid": true - } - ] - }, - { - "description": "enum with 1 does not match true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [1] - }, - "tests": [ - { - "description": "true is invalid", - "data": true, - "valid": false - }, - { - "description": "integer one is valid", - "data": 1, - "valid": true - }, - { - "description": "float one is valid", - "data": 1.0, - "valid": true - } - ] - }, - { - "description": "enum with [1] does not match [true]", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [[1]] - }, - "tests": [ - { - "description": "[true] is invalid", - "data": [true], - "valid": false - }, - { - "description": "[1] is valid", - "data": [1], - "valid": true - }, - { - "description": "[1.0] is valid", - "data": [1.0], - "valid": true - } - ] - }, - { - "description": "nul characters in strings", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "enum": [ "hello\u0000there" ] - }, - "tests": [ - { - "description": "match string with nul", - "data": "hello\u0000there", - "valid": true - }, - { - "description": "do not match string lacking nul", - "data": "hellothere", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/exclusiveMaximum.json b/jsonschema/testdata/draft2020-12/exclusiveMaximum.json deleted file mode 100644 index 05db2335..00000000 --- a/jsonschema/testdata/draft2020-12/exclusiveMaximum.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "description": "exclusiveMaximum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "exclusiveMaximum": 3.0 - }, - "tests": [ - { - "description": "below the exclusiveMaximum is valid", - "data": 2.2, - "valid": true - }, - { - "description": "boundary point is invalid", - "data": 3.0, - "valid": false - }, - { - "description": "above the exclusiveMaximum is invalid", - "data": 3.5, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "x", - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/exclusiveMinimum.json b/jsonschema/testdata/draft2020-12/exclusiveMinimum.json deleted file mode 100644 index 00af9d7f..00000000 --- a/jsonschema/testdata/draft2020-12/exclusiveMinimum.json +++ /dev/null @@ -1,31 +0,0 @@ -[ - { - "description": "exclusiveMinimum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "exclusiveMinimum": 1.1 - }, - "tests": [ - { - "description": "above the exclusiveMinimum is valid", - "data": 1.2, - "valid": true - }, - { - "description": "boundary point is invalid", - "data": 1.1, - "valid": false - }, - { - "description": "below the exclusiveMinimum is invalid", - "data": 0.6, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "x", - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/if-then-else.json b/jsonschema/testdata/draft2020-12/if-then-else.json deleted file mode 100644 index 1c35d7e6..00000000 --- a/jsonschema/testdata/draft2020-12/if-then-else.json +++ /dev/null @@ -1,268 +0,0 @@ -[ - { - "description": "ignore if without then or else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "const": 0 - } - }, - "tests": [ - { - "description": "valid when valid against lone if", - "data": 0, - "valid": true - }, - { - "description": "valid when invalid against lone if", - "data": "hello", - "valid": true - } - ] - }, - { - "description": "ignore then without if", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "then": { - "const": 0 - } - }, - "tests": [ - { - "description": "valid when valid against lone then", - "data": 0, - "valid": true - }, - { - "description": "valid when invalid against lone then", - "data": "hello", - "valid": true - } - ] - }, - { - "description": "ignore else without if", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "else": { - "const": 0 - } - }, - "tests": [ - { - "description": "valid when valid against lone else", - "data": 0, - "valid": true - }, - { - "description": "valid when invalid against lone else", - "data": "hello", - "valid": true - } - ] - }, - { - "description": "if and then without else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "exclusiveMaximum": 0 - }, - "then": { - "minimum": -10 - } - }, - "tests": [ - { - "description": "valid through then", - "data": -1, - "valid": true - }, - { - "description": "invalid through then", - "data": -100, - "valid": false - }, - { - "description": "valid when if test fails", - "data": 3, - "valid": true - } - ] - }, - { - "description": "if and else without then", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "exclusiveMaximum": 0 - }, - "else": { - "multipleOf": 2 - } - }, - "tests": [ - { - "description": "valid when if test passes", - "data": -1, - "valid": true - }, - { - "description": "valid through else", - "data": 4, - "valid": true - }, - { - "description": "invalid through else", - "data": 3, - "valid": false - } - ] - }, - { - "description": "validate against correct branch, then vs else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "exclusiveMaximum": 0 - }, - "then": { - "minimum": -10 - }, - "else": { - "multipleOf": 2 - } - }, - "tests": [ - { - "description": "valid through then", - "data": -1, - "valid": true - }, - { - "description": "invalid through then", - "data": -100, - "valid": false - }, - { - "description": "valid through else", - "data": 4, - "valid": true - }, - { - "description": "invalid through else", - "data": 3, - "valid": false - } - ] - }, - { - "description": "non-interference across combined schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "if": { - "exclusiveMaximum": 0 - } - }, - { - "then": { - "minimum": -10 - } - }, - { - "else": { - "multipleOf": 2 - } - } - ] - }, - "tests": [ - { - "description": "valid, but would have been invalid through then", - "data": -100, - "valid": true - }, - { - "description": "valid, but would have been invalid through else", - "data": 3, - "valid": true - } - ] - }, - { - "description": "if with boolean schema true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": true, - "then": { "const": "then" }, - "else": { "const": "else" } - }, - "tests": [ - { - "description": "boolean schema true in if always chooses the then path (valid)", - "data": "then", - "valid": true - }, - { - "description": "boolean schema true in if always chooses the then path (invalid)", - "data": "else", - "valid": false - } - ] - }, - { - "description": "if with boolean schema false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": false, - "then": { "const": "then" }, - "else": { "const": "else" } - }, - "tests": [ - { - "description": "boolean schema false in if always chooses the else path (invalid)", - "data": "then", - "valid": false - }, - { - "description": "boolean schema false in if always chooses the else path (valid)", - "data": "else", - "valid": true - } - ] - }, - { - "description": "if appears at the end when serialized (keyword processing sequence)", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "then": { "const": "yes" }, - "else": { "const": "other" }, - "if": { "maxLength": 4 } - }, - "tests": [ - { - "description": "yes redirects to then and passes", - "data": "yes", - "valid": true - }, - { - "description": "other redirects to else and passes", - "data": "other", - "valid": true - }, - { - "description": "no redirects to then and fails", - "data": "no", - "valid": false - }, - { - "description": "invalid redirects to else and fails", - "data": "invalid", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/infinite-loop-detection.json b/jsonschema/testdata/draft2020-12/infinite-loop-detection.json deleted file mode 100644 index 46f157a3..00000000 --- a/jsonschema/testdata/draft2020-12/infinite-loop-detection.json +++ /dev/null @@ -1,37 +0,0 @@ -[ - { - "description": "evaluating the same schema location against the same data location twice is not a sign of an infinite loop", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "int": { "type": "integer" } - }, - "allOf": [ - { - "properties": { - "foo": { - "$ref": "#/$defs/int" - } - } - }, - { - "additionalProperties": { - "$ref": "#/$defs/int" - } - } - ] - }, - "tests": [ - { - "description": "passing case", - "data": { "foo": 1 }, - "valid": true - }, - { - "description": "failing case", - "data": { "foo": "a string" }, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/items.json b/jsonschema/testdata/draft2020-12/items.json deleted file mode 100644 index 6a3e1cf2..00000000 --- a/jsonschema/testdata/draft2020-12/items.json +++ /dev/null @@ -1,304 +0,0 @@ -[ - { - "description": "a schema given for items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": {"type": "integer"} - }, - "tests": [ - { - "description": "valid items", - "data": [ 1, 2, 3 ], - "valid": true - }, - { - "description": "wrong type of items", - "data": [1, "x"], - "valid": false - }, - { - "description": "ignores non-arrays", - "data": {"foo" : "bar"}, - "valid": true - }, - { - "description": "JavaScript pseudo-array is valid", - "data": { - "0": "invalid", - "length": 1 - }, - "valid": true - } - ] - }, - { - "description": "items with boolean schema (true)", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": true - }, - "tests": [ - { - "description": "any array is valid", - "data": [ 1, "foo", true ], - "valid": true - }, - { - "description": "empty array is valid", - "data": [], - "valid": true - } - ] - }, - { - "description": "items with boolean schema (false)", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": false - }, - "tests": [ - { - "description": "any non-empty array is invalid", - "data": [ 1, "foo", true ], - "valid": false - }, - { - "description": "empty array is valid", - "data": [], - "valid": true - } - ] - }, - { - "description": "items and subitems", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "item": { - "type": "array", - "items": false, - "prefixItems": [ - { "$ref": "#/$defs/sub-item" }, - { "$ref": "#/$defs/sub-item" } - ] - }, - "sub-item": { - "type": "object", - "required": ["foo"] - } - }, - "type": "array", - "items": false, - "prefixItems": [ - { "$ref": "#/$defs/item" }, - { "$ref": "#/$defs/item" }, - { "$ref": "#/$defs/item" } - ] - }, - "tests": [ - { - "description": "valid items", - "data": [ - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ] - ], - "valid": true - }, - { - "description": "too many items", - "data": [ - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ] - ], - "valid": false - }, - { - "description": "too many sub-items", - "data": [ - [ {"foo": null}, {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ] - ], - "valid": false - }, - { - "description": "wrong item", - "data": [ - {"foo": null}, - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ] - ], - "valid": false - }, - { - "description": "wrong sub-item", - "data": [ - [ {}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ], - [ {"foo": null}, {"foo": null} ] - ], - "valid": false - }, - { - "description": "fewer items is valid", - "data": [ - [ {"foo": null} ], - [ {"foo": null} ] - ], - "valid": true - } - ] - }, - { - "description": "nested items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "number" - } - } - } - } - }, - "tests": [ - { - "description": "valid nested array", - "data": [[[[1]], [[2],[3]]], [[[4], [5], [6]]]], - "valid": true - }, - { - "description": "nested array with invalid type", - "data": [[[["1"]], [[2],[3]]], [[[4], [5], [6]]]], - "valid": false - }, - { - "description": "not deep enough", - "data": [[[1], [2],[3]], [[4], [5], [6]]], - "valid": false - } - ] - }, - { - "description": "prefixItems with no additional items allowed", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{}, {}, {}], - "items": false - }, - "tests": [ - { - "description": "empty array", - "data": [ ], - "valid": true - }, - { - "description": "fewer number of items present (1)", - "data": [ 1 ], - "valid": true - }, - { - "description": "fewer number of items present (2)", - "data": [ 1, 2 ], - "valid": true - }, - { - "description": "equal number of items present", - "data": [ 1, 2, 3 ], - "valid": true - }, - { - "description": "additional items are not permitted", - "data": [ 1, 2, 3, 4 ], - "valid": false - } - ] - }, - { - "description": "items does not look in applicators, valid case", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { "prefixItems": [ { "minimum": 3 } ] } - ], - "items": { "minimum": 5 } - }, - "tests": [ - { - "description": "prefixItems in allOf does not constrain items, invalid case", - "data": [ 3, 5 ], - "valid": false - }, - { - "description": "prefixItems in allOf does not constrain items, valid case", - "data": [ 5, 5 ], - "valid": true - } - ] - }, - { - "description": "prefixItems validation adjusts the starting index for items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ { "type": "string" } ], - "items": { "type": "integer" } - }, - "tests": [ - { - "description": "valid items", - "data": [ "x", 2, 3 ], - "valid": true - }, - { - "description": "wrong type of second item", - "data": [ "x", "y" ], - "valid": false - } - ] - }, - { - "description": "items with heterogeneous array", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{}], - "items": false - }, - "tests": [ - { - "description": "heterogeneous invalid instance", - "data": [ "foo", "bar", 37 ], - "valid": false - }, - { - "description": "valid instance", - "data": [ null ], - "valid": true - } - ] - }, - { - "description": "items with null instance elements", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": { - "type": "null" - } - }, - "tests": [ - { - "description": "allows null elements", - "data": [ null ], - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/maxContains.json b/jsonschema/testdata/draft2020-12/maxContains.json deleted file mode 100644 index 8cd3ca74..00000000 --- a/jsonschema/testdata/draft2020-12/maxContains.json +++ /dev/null @@ -1,102 +0,0 @@ -[ - { - "description": "maxContains without contains is ignored", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxContains": 1 - }, - "tests": [ - { - "description": "one item valid against lone maxContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "two items still valid against lone maxContains", - "data": [ 1, 2 ], - "valid": true - } - ] - }, - { - "description": "maxContains with contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "maxContains": 1 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": false - }, - { - "description": "all elements match, valid maxContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "all elements match, invalid maxContains", - "data": [ 1, 1 ], - "valid": false - }, - { - "description": "some elements match, valid maxContains", - "data": [ 1, 2 ], - "valid": true - }, - { - "description": "some elements match, invalid maxContains", - "data": [ 1, 2, 1 ], - "valid": false - } - ] - }, - { - "description": "maxContains with contains, value with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "maxContains": 1.0 - }, - "tests": [ - { - "description": "one element matches, valid maxContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "too many elements match, invalid maxContains", - "data": [ 1, 1 ], - "valid": false - } - ] - }, - { - "description": "minContains < maxContains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 1, - "maxContains": 3 - }, - "tests": [ - { - "description": "actual < minContains < maxContains", - "data": [ ], - "valid": false - }, - { - "description": "minContains < actual < maxContains", - "data": [ 1, 1 ], - "valid": true - }, - { - "description": "minContains < maxContains < actual", - "data": [ 1, 1, 1, 1 ], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/maxItems.json b/jsonschema/testdata/draft2020-12/maxItems.json deleted file mode 100644 index f6a6b7c9..00000000 --- a/jsonschema/testdata/draft2020-12/maxItems.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "description": "maxItems validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxItems": 2 - }, - "tests": [ - { - "description": "shorter is valid", - "data": [1], - "valid": true - }, - { - "description": "exact length is valid", - "data": [1, 2], - "valid": true - }, - { - "description": "too long is invalid", - "data": [1, 2, 3], - "valid": false - }, - { - "description": "ignores non-arrays", - "data": "foobar", - "valid": true - } - ] - }, - { - "description": "maxItems validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxItems": 2.0 - }, - "tests": [ - { - "description": "shorter is valid", - "data": [1], - "valid": true - }, - { - "description": "too long is invalid", - "data": [1, 2, 3], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/maxLength.json b/jsonschema/testdata/draft2020-12/maxLength.json deleted file mode 100644 index 7462726d..00000000 --- a/jsonschema/testdata/draft2020-12/maxLength.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "description": "maxLength validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxLength": 2 - }, - "tests": [ - { - "description": "shorter is valid", - "data": "f", - "valid": true - }, - { - "description": "exact length is valid", - "data": "fo", - "valid": true - }, - { - "description": "too long is invalid", - "data": "foo", - "valid": false - }, - { - "description": "ignores non-strings", - "data": 100, - "valid": true - }, - { - "description": "two graphemes is long enough", - "data": "\uD83D\uDCA9\uD83D\uDCA9", - "valid": true - } - ] - }, - { - "description": "maxLength validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxLength": 2.0 - }, - "tests": [ - { - "description": "shorter is valid", - "data": "f", - "valid": true - }, - { - "description": "too long is invalid", - "data": "foo", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/maxProperties.json b/jsonschema/testdata/draft2020-12/maxProperties.json deleted file mode 100644 index 73ae7316..00000000 --- a/jsonschema/testdata/draft2020-12/maxProperties.json +++ /dev/null @@ -1,79 +0,0 @@ -[ - { - "description": "maxProperties validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxProperties": 2 - }, - "tests": [ - { - "description": "shorter is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "exact length is valid", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "too long is invalid", - "data": {"foo": 1, "bar": 2, "baz": 3}, - "valid": false - }, - { - "description": "ignores arrays", - "data": [1, 2, 3], - "valid": true - }, - { - "description": "ignores strings", - "data": "foobar", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "maxProperties validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxProperties": 2.0 - }, - "tests": [ - { - "description": "shorter is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "too long is invalid", - "data": {"foo": 1, "bar": 2, "baz": 3}, - "valid": false - } - ] - }, - { - "description": "maxProperties = 0 means the object is empty", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maxProperties": 0 - }, - "tests": [ - { - "description": "no properties is valid", - "data": {}, - "valid": true - }, - { - "description": "one property is invalid", - "data": { "foo": 1 }, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/maximum.json b/jsonschema/testdata/draft2020-12/maximum.json deleted file mode 100644 index b99a541e..00000000 --- a/jsonschema/testdata/draft2020-12/maximum.json +++ /dev/null @@ -1,60 +0,0 @@ -[ - { - "description": "maximum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maximum": 3.0 - }, - "tests": [ - { - "description": "below the maximum is valid", - "data": 2.6, - "valid": true - }, - { - "description": "boundary point is valid", - "data": 3.0, - "valid": true - }, - { - "description": "above the maximum is invalid", - "data": 3.5, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "x", - "valid": true - } - ] - }, - { - "description": "maximum validation with unsigned integer", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "maximum": 300 - }, - "tests": [ - { - "description": "below the maximum is invalid", - "data": 299.97, - "valid": true - }, - { - "description": "boundary point integer is valid", - "data": 300, - "valid": true - }, - { - "description": "boundary point float is valid", - "data": 300.00, - "valid": true - }, - { - "description": "above the maximum is invalid", - "data": 300.5, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/minContains.json b/jsonschema/testdata/draft2020-12/minContains.json deleted file mode 100644 index ee72d7d6..00000000 --- a/jsonschema/testdata/draft2020-12/minContains.json +++ /dev/null @@ -1,224 +0,0 @@ -[ - { - "description": "minContains without contains is ignored", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minContains": 1 - }, - "tests": [ - { - "description": "one item valid against lone minContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "zero items still valid against lone minContains", - "data": [], - "valid": true - } - ] - }, - { - "description": "minContains=1 with contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 1 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": false - }, - { - "description": "no elements match", - "data": [ 2 ], - "valid": false - }, - { - "description": "single element matches, valid minContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "some elements match, valid minContains", - "data": [ 1, 2 ], - "valid": true - }, - { - "description": "all elements match, valid minContains", - "data": [ 1, 1 ], - "valid": true - } - ] - }, - { - "description": "minContains=2 with contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 2 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": false - }, - { - "description": "all elements match, invalid minContains", - "data": [ 1 ], - "valid": false - }, - { - "description": "some elements match, invalid minContains", - "data": [ 1, 2 ], - "valid": false - }, - { - "description": "all elements match, valid minContains (exactly as needed)", - "data": [ 1, 1 ], - "valid": true - }, - { - "description": "all elements match, valid minContains (more than needed)", - "data": [ 1, 1, 1 ], - "valid": true - }, - { - "description": "some elements match, valid minContains", - "data": [ 1, 2, 1 ], - "valid": true - } - ] - }, - { - "description": "minContains=2 with contains with a decimal value", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 2.0 - }, - "tests": [ - { - "description": "one element matches, invalid minContains", - "data": [ 1 ], - "valid": false - }, - { - "description": "both elements match, valid minContains", - "data": [ 1, 1 ], - "valid": true - } - ] - }, - { - "description": "maxContains = minContains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "maxContains": 2, - "minContains": 2 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": false - }, - { - "description": "all elements match, invalid minContains", - "data": [ 1 ], - "valid": false - }, - { - "description": "all elements match, invalid maxContains", - "data": [ 1, 1, 1 ], - "valid": false - }, - { - "description": "all elements match, valid maxContains and minContains", - "data": [ 1, 1 ], - "valid": true - } - ] - }, - { - "description": "maxContains < minContains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "maxContains": 1, - "minContains": 3 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": false - }, - { - "description": "invalid minContains", - "data": [ 1 ], - "valid": false - }, - { - "description": "invalid maxContains", - "data": [ 1, 1, 1 ], - "valid": false - }, - { - "description": "invalid maxContains and minContains", - "data": [ 1, 1 ], - "valid": false - } - ] - }, - { - "description": "minContains = 0", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 0 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": true - }, - { - "description": "minContains = 0 makes contains always pass", - "data": [ 2 ], - "valid": true - } - ] - }, - { - "description": "minContains = 0 with maxContains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "contains": {"const": 1}, - "minContains": 0, - "maxContains": 1 - }, - "tests": [ - { - "description": "empty data", - "data": [ ], - "valid": true - }, - { - "description": "not more than maxContains", - "data": [ 1 ], - "valid": true - }, - { - "description": "too many", - "data": [ 1, 1 ], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/minItems.json b/jsonschema/testdata/draft2020-12/minItems.json deleted file mode 100644 index 9d6a8b6d..00000000 --- a/jsonschema/testdata/draft2020-12/minItems.json +++ /dev/null @@ -1,50 +0,0 @@ -[ - { - "description": "minItems validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minItems": 1 - }, - "tests": [ - { - "description": "longer is valid", - "data": [1, 2], - "valid": true - }, - { - "description": "exact length is valid", - "data": [1], - "valid": true - }, - { - "description": "too short is invalid", - "data": [], - "valid": false - }, - { - "description": "ignores non-arrays", - "data": "", - "valid": true - } - ] - }, - { - "description": "minItems validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minItems": 1.0 - }, - "tests": [ - { - "description": "longer is valid", - "data": [1, 2], - "valid": true - }, - { - "description": "too short is invalid", - "data": [], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/minLength.json b/jsonschema/testdata/draft2020-12/minLength.json deleted file mode 100644 index 5076c5a9..00000000 --- a/jsonschema/testdata/draft2020-12/minLength.json +++ /dev/null @@ -1,55 +0,0 @@ -[ - { - "description": "minLength validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minLength": 2 - }, - "tests": [ - { - "description": "longer is valid", - "data": "foo", - "valid": true - }, - { - "description": "exact length is valid", - "data": "fo", - "valid": true - }, - { - "description": "too short is invalid", - "data": "f", - "valid": false - }, - { - "description": "ignores non-strings", - "data": 1, - "valid": true - }, - { - "description": "one grapheme is not long enough", - "data": "\uD83D\uDCA9", - "valid": false - } - ] - }, - { - "description": "minLength validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minLength": 2.0 - }, - "tests": [ - { - "description": "longer is valid", - "data": "foo", - "valid": true - }, - { - "description": "too short is invalid", - "data": "f", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/minProperties.json b/jsonschema/testdata/draft2020-12/minProperties.json deleted file mode 100644 index a753ad35..00000000 --- a/jsonschema/testdata/draft2020-12/minProperties.json +++ /dev/null @@ -1,60 +0,0 @@ -[ - { - "description": "minProperties validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minProperties": 1 - }, - "tests": [ - { - "description": "longer is valid", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "exact length is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "too short is invalid", - "data": {}, - "valid": false - }, - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores strings", - "data": "", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "minProperties validation with a decimal", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minProperties": 1.0 - }, - "tests": [ - { - "description": "longer is valid", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "too short is invalid", - "data": {}, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/minimum.json b/jsonschema/testdata/draft2020-12/minimum.json deleted file mode 100644 index dc440527..00000000 --- a/jsonschema/testdata/draft2020-12/minimum.json +++ /dev/null @@ -1,75 +0,0 @@ -[ - { - "description": "minimum validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minimum": 1.1 - }, - "tests": [ - { - "description": "above the minimum is valid", - "data": 2.6, - "valid": true - }, - { - "description": "boundary point is valid", - "data": 1.1, - "valid": true - }, - { - "description": "below the minimum is invalid", - "data": 0.6, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "x", - "valid": true - } - ] - }, - { - "description": "minimum validation with signed integer", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "minimum": -2 - }, - "tests": [ - { - "description": "negative above the minimum is valid", - "data": -1, - "valid": true - }, - { - "description": "positive above the minimum is valid", - "data": 0, - "valid": true - }, - { - "description": "boundary point is valid", - "data": -2, - "valid": true - }, - { - "description": "boundary point with float is valid", - "data": -2.0, - "valid": true - }, - { - "description": "float below the minimum is invalid", - "data": -2.0001, - "valid": false - }, - { - "description": "int below the minimum is invalid", - "data": -3, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "x", - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/multipleOf.json b/jsonschema/testdata/draft2020-12/multipleOf.json deleted file mode 100644 index 92d6979b..00000000 --- a/jsonschema/testdata/draft2020-12/multipleOf.json +++ /dev/null @@ -1,97 +0,0 @@ -[ - { - "description": "by int", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "multipleOf": 2 - }, - "tests": [ - { - "description": "int by int", - "data": 10, - "valid": true - }, - { - "description": "int by int fail", - "data": 7, - "valid": false - }, - { - "description": "ignores non-numbers", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "by number", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "multipleOf": 1.5 - }, - "tests": [ - { - "description": "zero is multiple of anything", - "data": 0, - "valid": true - }, - { - "description": "4.5 is multiple of 1.5", - "data": 4.5, - "valid": true - }, - { - "description": "35 is not multiple of 1.5", - "data": 35, - "valid": false - } - ] - }, - { - "description": "by small number", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "multipleOf": 0.0001 - }, - "tests": [ - { - "description": "0.0075 is multiple of 0.0001", - "data": 0.0075, - "valid": true - }, - { - "description": "0.00751 is not multiple of 0.0001", - "data": 0.00751, - "valid": false - } - ] - }, - { - "description": "float division = inf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer", "multipleOf": 0.123456789 - }, - "tests": [ - { - "description": "always invalid, but naive implementations may raise an overflow error", - "data": 1e308, - "valid": false - } - ] - }, - { - "description": "small multiple of large integer", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer", "multipleOf": 1e-8 - }, - "tests": [ - { - "description": "any integer is a multiple of 1e-8", - "data": 12391239123, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/not.json b/jsonschema/testdata/draft2020-12/not.json deleted file mode 100644 index 346d4a7e..00000000 --- a/jsonschema/testdata/draft2020-12/not.json +++ /dev/null @@ -1,301 +0,0 @@ -[ - { - "description": "not", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": {"type": "integer"} - }, - "tests": [ - { - "description": "allowed", - "data": "foo", - "valid": true - }, - { - "description": "disallowed", - "data": 1, - "valid": false - } - ] - }, - { - "description": "not multiple types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": {"type": ["integer", "boolean"]} - }, - "tests": [ - { - "description": "valid", - "data": "foo", - "valid": true - }, - { - "description": "mismatch", - "data": 1, - "valid": false - }, - { - "description": "other mismatch", - "data": true, - "valid": false - } - ] - }, - { - "description": "not more complex schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": { - "type": "object", - "properties": { - "foo": { - "type": "string" - } - } - } - }, - "tests": [ - { - "description": "match", - "data": 1, - "valid": true - }, - { - "description": "other match", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "mismatch", - "data": {"foo": "bar"}, - "valid": false - } - ] - }, - { - "description": "forbidden property", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": { - "not": {} - } - } - }, - "tests": [ - { - "description": "property present", - "data": {"foo": 1, "bar": 2}, - "valid": false - }, - { - "description": "property absent", - "data": {"bar": 1, "baz": 2}, - "valid": true - } - ] - }, - { - "description": "forbid everything with empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": {} - }, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - }, - { - "description": "boolean true is invalid", - "data": true, - "valid": false - }, - { - "description": "boolean false is invalid", - "data": false, - "valid": false - }, - { - "description": "null is invalid", - "data": null, - "valid": false - }, - { - "description": "object is invalid", - "data": {"foo": "bar"}, - "valid": false - }, - { - "description": "empty object is invalid", - "data": {}, - "valid": false - }, - { - "description": "array is invalid", - "data": ["foo"], - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - } - ] - }, - { - "description": "forbid everything with boolean schema true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": true - }, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - }, - { - "description": "boolean true is invalid", - "data": true, - "valid": false - }, - { - "description": "boolean false is invalid", - "data": false, - "valid": false - }, - { - "description": "null is invalid", - "data": null, - "valid": false - }, - { - "description": "object is invalid", - "data": {"foo": "bar"}, - "valid": false - }, - { - "description": "empty object is invalid", - "data": {}, - "valid": false - }, - { - "description": "array is invalid", - "data": ["foo"], - "valid": false - }, - { - "description": "empty array is invalid", - "data": [], - "valid": false - } - ] - }, - { - "description": "allow everything with boolean schema false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": false - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "string is valid", - "data": "foo", - "valid": true - }, - { - "description": "boolean true is valid", - "data": true, - "valid": true - }, - { - "description": "boolean false is valid", - "data": false, - "valid": true - }, - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "object is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - }, - { - "description": "array is valid", - "data": ["foo"], - "valid": true - }, - { - "description": "empty array is valid", - "data": [], - "valid": true - } - ] - }, - { - "description": "double negation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": { "not": {} } - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "collect annotations inside a 'not', even if collection is disabled", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "not": { - "$comment": "this subschema must still produce annotations internally, even though the 'not' will ultimately discard them", - "anyOf": [ - true, - { "properties": { "foo": true } } - ], - "unevaluatedProperties": false - } - }, - "tests": [ - { - "description": "unevaluated property", - "data": { "bar": 1 }, - "valid": true - }, - { - "description": "annotations are still collected inside a 'not'", - "data": { "foo": 1 }, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/oneOf.json b/jsonschema/testdata/draft2020-12/oneOf.json deleted file mode 100644 index 7a7c7ffe..00000000 --- a/jsonschema/testdata/draft2020-12/oneOf.json +++ /dev/null @@ -1,293 +0,0 @@ -[ - { - "description": "oneOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [ - { - "type": "integer" - }, - { - "minimum": 2 - } - ] - }, - "tests": [ - { - "description": "first oneOf valid", - "data": 1, - "valid": true - }, - { - "description": "second oneOf valid", - "data": 2.5, - "valid": true - }, - { - "description": "both oneOf valid", - "data": 3, - "valid": false - }, - { - "description": "neither oneOf valid", - "data": 1.5, - "valid": false - } - ] - }, - { - "description": "oneOf with base schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string", - "oneOf" : [ - { - "minLength": 2 - }, - { - "maxLength": 4 - } - ] - }, - "tests": [ - { - "description": "mismatch base schema", - "data": 3, - "valid": false - }, - { - "description": "one oneOf valid", - "data": "foobar", - "valid": true - }, - { - "description": "both oneOf valid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "oneOf with boolean schemas, all true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [true, true, true] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "oneOf with boolean schemas, one true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [true, false, false] - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "oneOf with boolean schemas, more than one true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [true, true, false] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "oneOf with boolean schemas, all false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [false, false, false] - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "oneOf complex types", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [ - { - "properties": { - "bar": {"type": "integer"} - }, - "required": ["bar"] - }, - { - "properties": { - "foo": {"type": "string"} - }, - "required": ["foo"] - } - ] - }, - "tests": [ - { - "description": "first oneOf valid (complex)", - "data": {"bar": 2}, - "valid": true - }, - { - "description": "second oneOf valid (complex)", - "data": {"foo": "baz"}, - "valid": true - }, - { - "description": "both oneOf valid (complex)", - "data": {"foo": "baz", "bar": 2}, - "valid": false - }, - { - "description": "neither oneOf valid (complex)", - "data": {"foo": 2, "bar": "quux"}, - "valid": false - } - ] - }, - { - "description": "oneOf with empty schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [ - { "type": "number" }, - {} - ] - }, - "tests": [ - { - "description": "one valid - valid", - "data": "foo", - "valid": true - }, - { - "description": "both valid - invalid", - "data": 123, - "valid": false - } - ] - }, - { - "description": "oneOf with required", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "oneOf": [ - { "required": ["foo", "bar"] }, - { "required": ["foo", "baz"] } - ] - }, - "tests": [ - { - "description": "both invalid - invalid", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "first valid - valid", - "data": {"foo": 1, "bar": 2}, - "valid": true - }, - { - "description": "second valid - valid", - "data": {"foo": 1, "baz": 3}, - "valid": true - }, - { - "description": "both valid - invalid", - "data": {"foo": 1, "bar": 2, "baz" : 3}, - "valid": false - } - ] - }, - { - "description": "oneOf with missing optional property", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [ - { - "properties": { - "bar": true, - "baz": true - }, - "required": ["bar"] - }, - { - "properties": { - "foo": true - }, - "required": ["foo"] - } - ] - }, - "tests": [ - { - "description": "first oneOf valid", - "data": {"bar": 8}, - "valid": true - }, - { - "description": "second oneOf valid", - "data": {"foo": "foo"}, - "valid": true - }, - { - "description": "both oneOf valid", - "data": {"foo": "foo", "bar": 8}, - "valid": false - }, - { - "description": "neither oneOf valid", - "data": {"baz": "quux"}, - "valid": false - } - ] - }, - { - "description": "nested oneOf, to check validation semantics", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "oneOf": [ - { - "oneOf": [ - { - "type": "null" - } - ] - } - ] - }, - "tests": [ - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "anything non-null is invalid", - "data": 123, - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/pattern.json b/jsonschema/testdata/draft2020-12/pattern.json deleted file mode 100644 index af0b8d89..00000000 --- a/jsonschema/testdata/draft2020-12/pattern.json +++ /dev/null @@ -1,65 +0,0 @@ -[ - { - "description": "pattern validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "pattern": "^a*$" - }, - "tests": [ - { - "description": "a matching pattern is valid", - "data": "aaa", - "valid": true - }, - { - "description": "a non-matching pattern is invalid", - "data": "abc", - "valid": false - }, - { - "description": "ignores booleans", - "data": true, - "valid": true - }, - { - "description": "ignores integers", - "data": 123, - "valid": true - }, - { - "description": "ignores floats", - "data": 1.0, - "valid": true - }, - { - "description": "ignores objects", - "data": {}, - "valid": true - }, - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores null", - "data": null, - "valid": true - } - ] - }, - { - "description": "pattern is not anchored", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "pattern": "a+" - }, - "tests": [ - { - "description": "matches a substring", - "data": "xxaayy", - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/patternProperties.json b/jsonschema/testdata/draft2020-12/patternProperties.json deleted file mode 100644 index 81829c71..00000000 --- a/jsonschema/testdata/draft2020-12/patternProperties.json +++ /dev/null @@ -1,176 +0,0 @@ -[ - { - "description": - "patternProperties validates properties matching a regex", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": { - "f.*o": {"type": "integer"} - } - }, - "tests": [ - { - "description": "a single valid match is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "multiple valid matches is valid", - "data": {"foo": 1, "foooooo" : 2}, - "valid": true - }, - { - "description": "a single invalid match is invalid", - "data": {"foo": "bar", "fooooo": 2}, - "valid": false - }, - { - "description": "multiple invalid matches is invalid", - "data": {"foo": "bar", "foooooo" : "baz"}, - "valid": false - }, - { - "description": "ignores arrays", - "data": ["foo"], - "valid": true - }, - { - "description": "ignores strings", - "data": "foo", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "multiple simultaneous patternProperties are validated", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": { - "a*": {"type": "integer"}, - "aaa*": {"maximum": 20} - } - }, - "tests": [ - { - "description": "a single valid match is valid", - "data": {"a": 21}, - "valid": true - }, - { - "description": "a simultaneous match is valid", - "data": {"aaaa": 18}, - "valid": true - }, - { - "description": "multiple matches is valid", - "data": {"a": 21, "aaaa": 18}, - "valid": true - }, - { - "description": "an invalid due to one is invalid", - "data": {"a": "bar"}, - "valid": false - }, - { - "description": "an invalid due to the other is invalid", - "data": {"aaaa": 31}, - "valid": false - }, - { - "description": "an invalid due to both is invalid", - "data": {"aaa": "foo", "aaaa": 31}, - "valid": false - } - ] - }, - { - "description": "regexes are not anchored by default and are case sensitive", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": { - "[0-9]{2,}": { "type": "boolean" }, - "X_": { "type": "string" } - } - }, - "tests": [ - { - "description": "non recognized members are ignored", - "data": { "answer 1": "42" }, - "valid": true - }, - { - "description": "recognized members are accounted for", - "data": { "a31b": null }, - "valid": false - }, - { - "description": "regexes are case sensitive", - "data": { "a_x_3": 3 }, - "valid": true - }, - { - "description": "regexes are case sensitive, 2", - "data": { "a_X_3": 3 }, - "valid": false - } - ] - }, - { - "description": "patternProperties with boolean schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": { - "f.*": true, - "b.*": false - } - }, - "tests": [ - { - "description": "object with property matching schema true is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "object with property matching schema false is invalid", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "object with both properties is invalid", - "data": {"foo": 1, "bar": 2}, - "valid": false - }, - { - "description": "object with a property matching both true and false is invalid", - "data": {"foobar":1}, - "valid": false - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "patternProperties with null valued instance properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "patternProperties": { - "^.*bar$": {"type": "null"} - } - }, - "tests": [ - { - "description": "allows null values", - "data": {"foobar": null}, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/prefixItems.json b/jsonschema/testdata/draft2020-12/prefixItems.json deleted file mode 100644 index 0adfc069..00000000 --- a/jsonschema/testdata/draft2020-12/prefixItems.json +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "description": "a schema given for prefixItems", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - {"type": "integer"}, - {"type": "string"} - ] - }, - "tests": [ - { - "description": "correct types", - "data": [ 1, "foo" ], - "valid": true - }, - { - "description": "wrong types", - "data": [ "foo", 1 ], - "valid": false - }, - { - "description": "incomplete array of items", - "data": [ 1 ], - "valid": true - }, - { - "description": "array with additional items", - "data": [ 1, "foo", true ], - "valid": true - }, - { - "description": "empty array", - "data": [ ], - "valid": true - }, - { - "description": "JavaScript pseudo-array is valid", - "data": { - "0": "invalid", - "1": "valid", - "length": 2 - }, - "valid": true - } - ] - }, - { - "description": "prefixItems with boolean schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [true, false] - }, - "tests": [ - { - "description": "array with one item is valid", - "data": [ 1 ], - "valid": true - }, - { - "description": "array with two items is invalid", - "data": [ 1, "foo" ], - "valid": false - }, - { - "description": "empty array is valid", - "data": [], - "valid": true - } - ] - }, - { - "description": "additional items are allowed by default", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{"type": "integer"}] - }, - "tests": [ - { - "description": "only the first item is validated", - "data": [1, "foo", false], - "valid": true - } - ] - }, - { - "description": "prefixItems with null instance elements", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { - "type": "null" - } - ] - }, - "tests": [ - { - "description": "allows null elements", - "data": [ null ], - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/properties.json b/jsonschema/testdata/draft2020-12/properties.json deleted file mode 100644 index 523dcde7..00000000 --- a/jsonschema/testdata/draft2020-12/properties.json +++ /dev/null @@ -1,242 +0,0 @@ -[ - { - "description": "object properties validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {"type": "integer"}, - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "both properties present and valid is valid", - "data": {"foo": 1, "bar": "baz"}, - "valid": true - }, - { - "description": "one property invalid is invalid", - "data": {"foo": 1, "bar": {}}, - "valid": false - }, - { - "description": "both properties invalid is invalid", - "data": {"foo": [], "bar": {}}, - "valid": false - }, - { - "description": "doesn't invalidate other properties", - "data": {"quux": []}, - "valid": true - }, - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": - "properties, patternProperties, additionalProperties interaction", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {"type": "array", "maxItems": 3}, - "bar": {"type": "array"} - }, - "patternProperties": {"f.o": {"minItems": 2}}, - "additionalProperties": {"type": "integer"} - }, - "tests": [ - { - "description": "property validates property", - "data": {"foo": [1, 2]}, - "valid": true - }, - { - "description": "property invalidates property", - "data": {"foo": [1, 2, 3, 4]}, - "valid": false - }, - { - "description": "patternProperty invalidates property", - "data": {"foo": []}, - "valid": false - }, - { - "description": "patternProperty validates nonproperty", - "data": {"fxo": [1, 2]}, - "valid": true - }, - { - "description": "patternProperty invalidates nonproperty", - "data": {"fxo": []}, - "valid": false - }, - { - "description": "additionalProperty ignores property", - "data": {"bar": []}, - "valid": true - }, - { - "description": "additionalProperty validates others", - "data": {"quux": 3}, - "valid": true - }, - { - "description": "additionalProperty invalidates others", - "data": {"quux": "foo"}, - "valid": false - } - ] - }, - { - "description": "properties with boolean schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": true, - "bar": false - } - }, - "tests": [ - { - "description": "no property present is valid", - "data": {}, - "valid": true - }, - { - "description": "only 'true' property present is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "only 'false' property present is invalid", - "data": {"bar": 2}, - "valid": false - }, - { - "description": "both properties present is invalid", - "data": {"foo": 1, "bar": 2}, - "valid": false - } - ] - }, - { - "description": "properties with escaped characters", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo\nbar": {"type": "number"}, - "foo\"bar": {"type": "number"}, - "foo\\bar": {"type": "number"}, - "foo\rbar": {"type": "number"}, - "foo\tbar": {"type": "number"}, - "foo\fbar": {"type": "number"} - } - }, - "tests": [ - { - "description": "object with all numbers is valid", - "data": { - "foo\nbar": 1, - "foo\"bar": 1, - "foo\\bar": 1, - "foo\rbar": 1, - "foo\tbar": 1, - "foo\fbar": 1 - }, - "valid": true - }, - { - "description": "object with strings is invalid", - "data": { - "foo\nbar": "1", - "foo\"bar": "1", - "foo\\bar": "1", - "foo\rbar": "1", - "foo\tbar": "1", - "foo\fbar": "1" - }, - "valid": false - } - ] - }, - { - "description": "properties with null valued instance properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {"type": "null"} - } - }, - "tests": [ - { - "description": "allows null values", - "data": {"foo": null}, - "valid": true - } - ] - }, - { - "description": "properties whose names are Javascript object property names", - "comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "__proto__": {"type": "number"}, - "toString": { - "properties": { "length": { "type": "string" } } - }, - "constructor": {"type": "number"} - } - }, - "tests": [ - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - }, - { - "description": "none of the properties mentioned", - "data": {}, - "valid": true - }, - { - "description": "__proto__ not valid", - "data": { "__proto__": "foo" }, - "valid": false - }, - { - "description": "toString not valid", - "data": { "toString": { "length": 37 } }, - "valid": false - }, - { - "description": "constructor not valid", - "data": { "constructor": { "length": 37 } }, - "valid": false - }, - { - "description": "all present and valid", - "data": { - "__proto__": 12, - "toString": { "length": "foo" }, - "constructor": 37 - }, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/propertyNames.json b/jsonschema/testdata/draft2020-12/propertyNames.json deleted file mode 100644 index b4780088..00000000 --- a/jsonschema/testdata/draft2020-12/propertyNames.json +++ /dev/null @@ -1,168 +0,0 @@ -[ - { - "description": "propertyNames validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": {"maxLength": 3} - }, - "tests": [ - { - "description": "all property names valid", - "data": { - "f": {}, - "foo": {} - }, - "valid": true - }, - { - "description": "some property names invalid", - "data": { - "foo": {}, - "foobar": {} - }, - "valid": false - }, - { - "description": "object without properties is valid", - "data": {}, - "valid": true - }, - { - "description": "ignores arrays", - "data": [1, 2, 3, 4], - "valid": true - }, - { - "description": "ignores strings", - "data": "foobar", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "propertyNames validation with pattern", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": { "pattern": "^a+$" } - }, - "tests": [ - { - "description": "matching property names valid", - "data": { - "a": {}, - "aa": {}, - "aaa": {} - }, - "valid": true - }, - { - "description": "non-matching property name is invalid", - "data": { - "aaA": {} - }, - "valid": false - }, - { - "description": "object without properties is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "propertyNames with boolean schema true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": true - }, - "tests": [ - { - "description": "object with any properties is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "propertyNames with boolean schema false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": false - }, - "tests": [ - { - "description": "object with any properties is invalid", - "data": {"foo": 1}, - "valid": false - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "propertyNames with const", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": {"const": "foo"} - }, - "tests": [ - { - "description": "object with property foo is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "object with any other property is invalid", - "data": {"bar": 1}, - "valid": false - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - }, - { - "description": "propertyNames with enum", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": {"enum": ["foo", "bar"]} - }, - "tests": [ - { - "description": "object with property foo is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "object with property foo and bar is valid", - "data": {"foo": 1, "bar": 1}, - "valid": true - }, - { - "description": "object with any other property is invalid", - "data": {"baz": 1}, - "valid": false - }, - { - "description": "empty object is valid", - "data": {}, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/ref.json b/jsonschema/testdata/draft2020-12/ref.json deleted file mode 100644 index 0ac02fb9..00000000 --- a/jsonschema/testdata/draft2020-12/ref.json +++ /dev/null @@ -1,1052 +0,0 @@ -[ - { - "description": "root pointer ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {"$ref": "#"} - }, - "additionalProperties": false - }, - "tests": [ - { - "description": "match", - "data": {"foo": false}, - "valid": true - }, - { - "description": "recursive match", - "data": {"foo": {"foo": false}}, - "valid": true - }, - { - "description": "mismatch", - "data": {"bar": false}, - "valid": false - }, - { - "description": "recursive mismatch", - "data": {"foo": {"bar": false}}, - "valid": false - } - ] - }, - { - "description": "relative pointer ref to object", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {"type": "integer"}, - "bar": {"$ref": "#/properties/foo"} - } - }, - "tests": [ - { - "description": "match", - "data": {"bar": 3}, - "valid": true - }, - { - "description": "mismatch", - "data": {"bar": true}, - "valid": false - } - ] - }, - { - "description": "relative pointer ref to array", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - {"type": "integer"}, - {"$ref": "#/prefixItems/0"} - ] - }, - "tests": [ - { - "description": "match array", - "data": [1, 2], - "valid": true - }, - { - "description": "mismatch array", - "data": [1, "foo"], - "valid": false - } - ] - }, - { - "description": "escaped pointer ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "tilde~field": {"type": "integer"}, - "slash/field": {"type": "integer"}, - "percent%field": {"type": "integer"} - }, - "properties": { - "tilde": {"$ref": "#/$defs/tilde~0field"}, - "slash": {"$ref": "#/$defs/slash~1field"}, - "percent": {"$ref": "#/$defs/percent%25field"} - } - }, - "tests": [ - { - "description": "slash invalid", - "data": {"slash": "aoeu"}, - "valid": false - }, - { - "description": "tilde invalid", - "data": {"tilde": "aoeu"}, - "valid": false - }, - { - "description": "percent invalid", - "data": {"percent": "aoeu"}, - "valid": false - }, - { - "description": "slash valid", - "data": {"slash": 123}, - "valid": true - }, - { - "description": "tilde valid", - "data": {"tilde": 123}, - "valid": true - }, - { - "description": "percent valid", - "data": {"percent": 123}, - "valid": true - } - ] - }, - { - "description": "nested refs", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "a": {"type": "integer"}, - "b": {"$ref": "#/$defs/a"}, - "c": {"$ref": "#/$defs/b"} - }, - "$ref": "#/$defs/c" - }, - "tests": [ - { - "description": "nested ref valid", - "data": 5, - "valid": true - }, - { - "description": "nested ref invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "ref applies alongside sibling keywords", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "reffed": { - "type": "array" - } - }, - "properties": { - "foo": { - "$ref": "#/$defs/reffed", - "maxItems": 2 - } - } - }, - "tests": [ - { - "description": "ref valid, maxItems valid", - "data": { "foo": [] }, - "valid": true - }, - { - "description": "ref valid, maxItems invalid", - "data": { "foo": [1, 2, 3] }, - "valid": false - }, - { - "description": "ref invalid", - "data": { "foo": "string" }, - "valid": false - } - ] - }, - { - "description": "remote ref, containing refs itself", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "https://json-schema.org/draft/2020-12/schema" - }, - "tests": [ - { - "description": "remote ref valid", - "data": {"minLength": 1}, - "valid": true - }, - { - "description": "remote ref invalid", - "data": {"minLength": -1}, - "valid": false - } - ] - }, - { - "description": "property named $ref that is not a reference", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "$ref": {"type": "string"} - } - }, - "tests": [ - { - "description": "property named $ref valid", - "data": {"$ref": "a"}, - "valid": true - }, - { - "description": "property named $ref invalid", - "data": {"$ref": 2}, - "valid": false - } - ] - }, - { - "description": "property named $ref, containing an actual $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "$ref": {"$ref": "#/$defs/is-string"} - }, - "$defs": { - "is-string": { - "type": "string" - } - } - }, - "tests": [ - { - "description": "property named $ref valid", - "data": {"$ref": "a"}, - "valid": true - }, - { - "description": "property named $ref invalid", - "data": {"$ref": 2}, - "valid": false - } - ] - }, - { - "description": "$ref to boolean schema true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "#/$defs/bool", - "$defs": { - "bool": true - } - }, - "tests": [ - { - "description": "any value is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "$ref to boolean schema false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "#/$defs/bool", - "$defs": { - "bool": false - } - }, - "tests": [ - { - "description": "any value is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "Recursive references between schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/tree", - "description": "tree of nodes", - "type": "object", - "properties": { - "meta": {"type": "string"}, - "nodes": { - "type": "array", - "items": {"$ref": "node"} - } - }, - "required": ["meta", "nodes"], - "$defs": { - "node": { - "$id": "http://localhost:1234/draft2020-12/node", - "description": "node", - "type": "object", - "properties": { - "value": {"type": "number"}, - "subtree": {"$ref": "tree"} - }, - "required": ["value"] - } - } - }, - "tests": [ - { - "description": "valid tree", - "data": { - "meta": "root", - "nodes": [ - { - "value": 1, - "subtree": { - "meta": "child", - "nodes": [ - {"value": 1.1}, - {"value": 1.2} - ] - } - }, - { - "value": 2, - "subtree": { - "meta": "child", - "nodes": [ - {"value": 2.1}, - {"value": 2.2} - ] - } - } - ] - }, - "valid": true - }, - { - "description": "invalid tree", - "data": { - "meta": "root", - "nodes": [ - { - "value": 1, - "subtree": { - "meta": "child", - "nodes": [ - {"value": "string is invalid"}, - {"value": 1.2} - ] - } - }, - { - "value": 2, - "subtree": { - "meta": "child", - "nodes": [ - {"value": 2.1}, - {"value": 2.2} - ] - } - } - ] - }, - "valid": false - } - ] - }, - { - "description": "refs with quote", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo\"bar": {"$ref": "#/$defs/foo%22bar"} - }, - "$defs": { - "foo\"bar": {"type": "number"} - } - }, - "tests": [ - { - "description": "object with numbers is valid", - "data": { - "foo\"bar": 1 - }, - "valid": true - }, - { - "description": "object with strings is invalid", - "data": { - "foo\"bar": "1" - }, - "valid": false - } - ] - }, - { - "description": "ref creates new scope when adjacent to keywords", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "A": { - "unevaluatedProperties": false - } - }, - "properties": { - "prop1": { - "type": "string" - } - }, - "$ref": "#/$defs/A" - }, - "tests": [ - { - "description": "referenced subschema doesn't see annotations from properties", - "data": { - "prop1": "match" - }, - "valid": false - } - ] - }, - { - "description": "naive replacement of $ref with its destination is not correct", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "a_string": { "type": "string" } - }, - "enum": [ - { "$ref": "#/$defs/a_string" } - ] - }, - "tests": [ - { - "description": "do not evaluate the $ref inside the enum, matching any string", - "data": "this is a string", - "valid": false - }, - { - "description": "do not evaluate the $ref inside the enum, definition exact match", - "data": { "type": "string" }, - "valid": false - }, - { - "description": "match the enum exactly", - "data": { "$ref": "#/$defs/a_string" }, - "valid": true - } - ] - }, - { - "description": "refs with relative uris and defs", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://example.com/schema-relative-uri-defs1.json", - "properties": { - "foo": { - "$id": "schema-relative-uri-defs2.json", - "$defs": { - "inner": { - "properties": { - "bar": { "type": "string" } - } - } - }, - "$ref": "#/$defs/inner" - } - }, - "$ref": "schema-relative-uri-defs2.json" - }, - "tests": [ - { - "description": "invalid on inner field", - "data": { - "foo": { - "bar": 1 - }, - "bar": "a" - }, - "valid": false - }, - { - "description": "invalid on outer field", - "data": { - "foo": { - "bar": "a" - }, - "bar": 1 - }, - "valid": false - }, - { - "description": "valid on both fields", - "data": { - "foo": { - "bar": "a" - }, - "bar": "a" - }, - "valid": true - } - ] - }, - { - "description": "relative refs with absolute uris and defs", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://example.com/schema-refs-absolute-uris-defs1.json", - "properties": { - "foo": { - "$id": "http://example.com/schema-refs-absolute-uris-defs2.json", - "$defs": { - "inner": { - "properties": { - "bar": { "type": "string" } - } - } - }, - "$ref": "#/$defs/inner" - } - }, - "$ref": "schema-refs-absolute-uris-defs2.json" - }, - "tests": [ - { - "description": "invalid on inner field", - "data": { - "foo": { - "bar": 1 - }, - "bar": "a" - }, - "valid": false - }, - { - "description": "invalid on outer field", - "data": { - "foo": { - "bar": "a" - }, - "bar": 1 - }, - "valid": false - }, - { - "description": "valid on both fields", - "data": { - "foo": { - "bar": "a" - }, - "bar": "a" - }, - "valid": true - } - ] - }, - { - "description": "$id must be resolved against nearest parent, not just immediate parent", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://example.com/a.json", - "$defs": { - "x": { - "$id": "http://example.com/b/c.json", - "not": { - "$defs": { - "y": { - "$id": "d.json", - "type": "number" - } - } - } - } - }, - "allOf": [ - { - "$ref": "http://example.com/b/d.json" - } - ] - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "order of evaluation: $id and $ref", - "schema": { - "$comment": "$id must be evaluated before $ref to get the proper $ref destination", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/draft2020-12/ref-and-id1/base.json", - "$ref": "int.json", - "$defs": { - "bigint": { - "$comment": "canonical uri: https://example.com/ref-and-id1/int.json", - "$id": "int.json", - "maximum": 10 - }, - "smallint": { - "$comment": "canonical uri: https://example.com/ref-and-id1-int.json", - "$id": "/draft2020-12/ref-and-id1-int.json", - "maximum": 2 - } - } - }, - "tests": [ - { - "description": "data is valid against first definition", - "data": 5, - "valid": true - }, - { - "description": "data is invalid against first definition", - "data": 50, - "valid": false - } - ] - }, - { - "description": "order of evaluation: $id and $anchor and $ref", - "schema": { - "$comment": "$id must be evaluated before $ref to get the proper $ref destination", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/draft2020-12/ref-and-id2/base.json", - "$ref": "#bigint", - "$defs": { - "bigint": { - "$comment": "canonical uri: /ref-and-id2/base.json#/$defs/bigint; another valid uri for this location: /ref-and-id2/base.json#bigint", - "$anchor": "bigint", - "maximum": 10 - }, - "smallint": { - "$comment": "canonical uri: https://example.com/ref-and-id2#/$defs/smallint; another valid uri for this location: https://example.com/ref-and-id2/#bigint", - "$id": "https://example.com/draft2020-12/ref-and-id2/", - "$anchor": "bigint", - "maximum": 2 - } - } - }, - "tests": [ - { - "description": "data is valid against first definition", - "data": 5, - "valid": true - }, - { - "description": "data is invalid against first definition", - "data": 50, - "valid": false - } - ] - }, - { - "description": "simple URN base URI with $ref via the URN", - "schema": { - "$comment": "URIs do not have to have HTTP(s) schemes", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:uuid:deadbeef-1234-ffff-ffff-4321feebdaed", - "minimum": 30, - "properties": { - "foo": {"$ref": "urn:uuid:deadbeef-1234-ffff-ffff-4321feebdaed"} - } - }, - "tests": [ - { - "description": "valid under the URN IDed schema", - "data": {"foo": 37}, - "valid": true - }, - { - "description": "invalid under the URN IDed schema", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "simple URN base URI with JSON pointer", - "schema": { - "$comment": "URIs do not have to have HTTP(s) schemes", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:uuid:deadbeef-1234-00ff-ff00-4321feebdaed", - "properties": { - "foo": {"$ref": "#/$defs/bar"} - }, - "$defs": { - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN base URI with NSS", - "schema": { - "$comment": "RFC 8141 ยง2.2", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:example:1/406/47452/2", - "properties": { - "foo": {"$ref": "#/$defs/bar"} - }, - "$defs": { - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN base URI with r-component", - "schema": { - "$comment": "RFC 8141 ยง2.3.1", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:example:foo-bar-baz-qux?+CCResolve:cc=uk", - "properties": { - "foo": {"$ref": "#/$defs/bar"} - }, - "$defs": { - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN base URI with q-component", - "schema": { - "$comment": "RFC 8141 ยง2.3.2", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:example:weather?=op=map&lat=39.56&lon=-104.85&datetime=1969-07-21T02:56:15Z", - "properties": { - "foo": {"$ref": "#/$defs/bar"} - }, - "$defs": { - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN base URI with URN and JSON pointer ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:uuid:deadbeef-1234-0000-0000-4321feebdaed", - "properties": { - "foo": {"$ref": "urn:uuid:deadbeef-1234-0000-0000-4321feebdaed#/$defs/bar"} - }, - "$defs": { - "bar": {"type": "string"} - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN base URI with URN and anchor ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:uuid:deadbeef-1234-ff00-00ff-4321feebdaed", - "properties": { - "foo": {"$ref": "urn:uuid:deadbeef-1234-ff00-00ff-4321feebdaed#something"} - }, - "$defs": { - "bar": { - "$anchor": "something", - "type": "string" - } - } - }, - "tests": [ - { - "description": "a string is valid", - "data": {"foo": "bar"}, - "valid": true - }, - { - "description": "a non-string is invalid", - "data": {"foo": 12}, - "valid": false - } - ] - }, - { - "description": "URN ref with nested pointer ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "urn:uuid:deadbeef-4321-ffff-ffff-1234feebdaed", - "$defs": { - "foo": { - "$id": "urn:uuid:deadbeef-4321-ffff-ffff-1234feebdaed", - "$defs": {"bar": {"type": "string"}}, - "$ref": "#/$defs/bar" - } - } - }, - "tests": [ - { - "description": "a string is valid", - "data": "bar", - "valid": true - }, - { - "description": "a non-string is invalid", - "data": 12, - "valid": false - } - ] - }, - { - "description": "ref to if", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://example.com/ref/if", - "if": { - "$id": "http://example.com/ref/if", - "type": "integer" - } - }, - "tests": [ - { - "description": "a non-integer is invalid due to the $ref", - "data": "foo", - "valid": false - }, - { - "description": "an integer is valid", - "data": 12, - "valid": true - } - ] - }, - { - "description": "ref to then", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://example.com/ref/then", - "then": { - "$id": "http://example.com/ref/then", - "type": "integer" - } - }, - "tests": [ - { - "description": "a non-integer is invalid due to the $ref", - "data": "foo", - "valid": false - }, - { - "description": "an integer is valid", - "data": 12, - "valid": true - } - ] - }, - { - "description": "ref to else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://example.com/ref/else", - "else": { - "$id": "http://example.com/ref/else", - "type": "integer" - } - }, - "tests": [ - { - "description": "a non-integer is invalid due to the $ref", - "data": "foo", - "valid": false - }, - { - "description": "an integer is valid", - "data": 12, - "valid": true - } - ] - }, - { - "description": "ref with absolute-path-reference", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://example.com/ref/absref.json", - "$defs": { - "a": { - "$id": "http://example.com/ref/absref/foobar.json", - "type": "number" - }, - "b": { - "$id": "http://example.com/absref/foobar.json", - "type": "string" - } - }, - "$ref": "/absref/foobar.json" - }, - "tests": [ - { - "description": "a string is valid", - "data": "foo", - "valid": true - }, - { - "description": "an integer is invalid", - "data": 12, - "valid": false - } - ] - }, - { - "description": "$id with file URI still resolves pointers - *nix", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "file:///folder/file.json", - "$defs": { - "foo": { - "type": "number" - } - }, - "$ref": "#/$defs/foo" - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "$id with file URI still resolves pointers - windows", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "file:///c:/folder/file.json", - "$defs": { - "foo": { - "type": "number" - } - }, - "$ref": "#/$defs/foo" - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "empty tokens in $ref json-pointer", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "": { - "$defs": { - "": { "type": "number" } - } - } - }, - "allOf": [ - { - "$ref": "#/$defs//$defs/" - } - ] - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/refRemote.json b/jsonschema/testdata/draft2020-12/refRemote.json deleted file mode 100644 index 047ac74c..00000000 --- a/jsonschema/testdata/draft2020-12/refRemote.json +++ /dev/null @@ -1,342 +0,0 @@ -[ - { - "description": "remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/integer.json" - }, - "tests": [ - { - "description": "remote ref valid", - "data": 1, - "valid": true - }, - { - "description": "remote ref invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "fragment within remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/subSchemas.json#/$defs/integer" - }, - "tests": [ - { - "description": "remote fragment valid", - "data": 1, - "valid": true - }, - { - "description": "remote fragment invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "anchor within remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/locationIndependentIdentifier.json#foo" - }, - "tests": [ - { - "description": "remote anchor valid", - "data": 1, - "valid": true - }, - { - "description": "remote anchor invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "ref within remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/subSchemas.json#/$defs/refToInteger" - }, - "tests": [ - { - "description": "ref within ref valid", - "data": 1, - "valid": true - }, - { - "description": "ref within ref invalid", - "data": "a", - "valid": false - } - ] - }, - { - "description": "base URI change", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/", - "items": { - "$id": "baseUriChange/", - "items": {"$ref": "folderInteger.json"} - } - }, - "tests": [ - { - "description": "base URI change ref valid", - "data": [[1]], - "valid": true - }, - { - "description": "base URI change ref invalid", - "data": [["a"]], - "valid": false - } - ] - }, - { - "description": "base URI change - change folder", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/scope_change_defs1.json", - "type" : "object", - "properties": {"list": {"$ref": "baseUriChangeFolder/"}}, - "$defs": { - "baz": { - "$id": "baseUriChangeFolder/", - "type": "array", - "items": {"$ref": "folderInteger.json"} - } - } - }, - "tests": [ - { - "description": "number is valid", - "data": {"list": [1]}, - "valid": true - }, - { - "description": "string is invalid", - "data": {"list": ["a"]}, - "valid": false - } - ] - }, - { - "description": "base URI change - change folder in subschema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/scope_change_defs2.json", - "type" : "object", - "properties": {"list": {"$ref": "baseUriChangeFolderInSubschema/#/$defs/bar"}}, - "$defs": { - "baz": { - "$id": "baseUriChangeFolderInSubschema/", - "$defs": { - "bar": { - "type": "array", - "items": {"$ref": "folderInteger.json"} - } - } - } - } - }, - "tests": [ - { - "description": "number is valid", - "data": {"list": [1]}, - "valid": true - }, - { - "description": "string is invalid", - "data": {"list": ["a"]}, - "valid": false - } - ] - }, - { - "description": "root ref in remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/object", - "type": "object", - "properties": { - "name": {"$ref": "name-defs.json#/$defs/orNull"} - } - }, - "tests": [ - { - "description": "string is valid", - "data": { - "name": "foo" - }, - "valid": true - }, - { - "description": "null is valid", - "data": { - "name": null - }, - "valid": true - }, - { - "description": "object is invalid", - "data": { - "name": { - "name": null - } - }, - "valid": false - } - ] - }, - { - "description": "remote ref with ref to defs", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/schema-remote-ref-ref-defs1.json", - "$ref": "ref-and-defs.json" - }, - "tests": [ - { - "description": "invalid", - "data": { - "bar": 1 - }, - "valid": false - }, - { - "description": "valid", - "data": { - "bar": "a" - }, - "valid": true - } - ] - }, - { - "description": "Location-independent identifier in remote ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/locationIndependentIdentifier.json#/$defs/refToInteger" - }, - "tests": [ - { - "description": "integer is valid", - "data": 1, - "valid": true - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - } - ] - }, - { - "description": "retrieved nested refs resolve relative to their URI not $id", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/some-id", - "properties": { - "name": {"$ref": "nested/foo-ref-string.json"} - } - }, - "tests": [ - { - "description": "number is invalid", - "data": { - "name": {"foo": 1} - }, - "valid": false - }, - { - "description": "string is valid", - "data": { - "name": {"foo": "a"} - }, - "valid": true - } - ] - }, - { - "description": "remote HTTP ref with different $id", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/different-id-ref-string.json" - }, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "remote HTTP ref with different URN $id", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/urn-ref-string.json" - }, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "remote HTTP ref with nested absolute ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/nested-absolute-ref-to-string.json" - }, - "tests": [ - { - "description": "number is invalid", - "data": 1, - "valid": false - }, - { - "description": "string is valid", - "data": "foo", - "valid": true - } - ] - }, - { - "description": "$ref to $ref finds detached $anchor", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "http://localhost:1234/draft2020-12/detached-ref.json#/$defs/foo" - }, - "tests": [ - { - "description": "number is valid", - "data": 1, - "valid": true - }, - { - "description": "non-number is invalid", - "data": "a", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/required.json b/jsonschema/testdata/draft2020-12/required.json deleted file mode 100644 index e66f29f2..00000000 --- a/jsonschema/testdata/draft2020-12/required.json +++ /dev/null @@ -1,158 +0,0 @@ -[ - { - "description": "required validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {}, - "bar": {} - }, - "required": ["foo"] - }, - "tests": [ - { - "description": "present required property is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "non-present required property is invalid", - "data": {"bar": 1}, - "valid": false - }, - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores strings", - "data": "", - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "required default validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {} - } - }, - "tests": [ - { - "description": "not required by default", - "data": {}, - "valid": true - } - ] - }, - { - "description": "required with empty array", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": {} - }, - "required": [] - }, - "tests": [ - { - "description": "property not required", - "data": {}, - "valid": true - } - ] - }, - { - "description": "required with escaped characters", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "required": [ - "foo\nbar", - "foo\"bar", - "foo\\bar", - "foo\rbar", - "foo\tbar", - "foo\fbar" - ] - }, - "tests": [ - { - "description": "object with all properties present is valid", - "data": { - "foo\nbar": 1, - "foo\"bar": 1, - "foo\\bar": 1, - "foo\rbar": 1, - "foo\tbar": 1, - "foo\fbar": 1 - }, - "valid": true - }, - { - "description": "object with some properties missing is invalid", - "data": { - "foo\nbar": "1", - "foo\"bar": "1" - }, - "valid": false - } - ] - }, - { - "description": "required properties whose names are Javascript object property names", - "comment": "Ensure JS implementations don't universally consider e.g. __proto__ to always be present in an object.", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "required": ["__proto__", "toString", "constructor"] - }, - "tests": [ - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores other non-objects", - "data": 12, - "valid": true - }, - { - "description": "none of the properties mentioned", - "data": {}, - "valid": false - }, - { - "description": "__proto__ present", - "data": { "__proto__": "foo" }, - "valid": false - }, - { - "description": "toString present", - "data": { "toString": { "length": 37 } }, - "valid": false - }, - { - "description": "constructor present", - "data": { "constructor": { "length": 37 } }, - "valid": false - }, - { - "description": "all present", - "data": { - "__proto__": 12, - "toString": { "length": "foo" }, - "constructor": 37 - }, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/type.json b/jsonschema/testdata/draft2020-12/type.json deleted file mode 100644 index 2123c408..00000000 --- a/jsonschema/testdata/draft2020-12/type.json +++ /dev/null @@ -1,501 +0,0 @@ -[ - { - "description": "integer type matches integers", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer" - }, - "tests": [ - { - "description": "an integer is an integer", - "data": 1, - "valid": true - }, - { - "description": "a float with zero fractional part is an integer", - "data": 1.0, - "valid": true - }, - { - "description": "a float is not an integer", - "data": 1.1, - "valid": false - }, - { - "description": "a string is not an integer", - "data": "foo", - "valid": false - }, - { - "description": "a string is still not an integer, even if it looks like one", - "data": "1", - "valid": false - }, - { - "description": "an object is not an integer", - "data": {}, - "valid": false - }, - { - "description": "an array is not an integer", - "data": [], - "valid": false - }, - { - "description": "a boolean is not an integer", - "data": true, - "valid": false - }, - { - "description": "null is not an integer", - "data": null, - "valid": false - } - ] - }, - { - "description": "number type matches numbers", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "number" - }, - "tests": [ - { - "description": "an integer is a number", - "data": 1, - "valid": true - }, - { - "description": "a float with zero fractional part is a number (and an integer)", - "data": 1.0, - "valid": true - }, - { - "description": "a float is a number", - "data": 1.1, - "valid": true - }, - { - "description": "a string is not a number", - "data": "foo", - "valid": false - }, - { - "description": "a string is still not a number, even if it looks like one", - "data": "1", - "valid": false - }, - { - "description": "an object is not a number", - "data": {}, - "valid": false - }, - { - "description": "an array is not a number", - "data": [], - "valid": false - }, - { - "description": "a boolean is not a number", - "data": true, - "valid": false - }, - { - "description": "null is not a number", - "data": null, - "valid": false - } - ] - }, - { - "description": "string type matches strings", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string" - }, - "tests": [ - { - "description": "1 is not a string", - "data": 1, - "valid": false - }, - { - "description": "a float is not a string", - "data": 1.1, - "valid": false - }, - { - "description": "a string is a string", - "data": "foo", - "valid": true - }, - { - "description": "a string is still a string, even if it looks like a number", - "data": "1", - "valid": true - }, - { - "description": "an empty string is still a string", - "data": "", - "valid": true - }, - { - "description": "an object is not a string", - "data": {}, - "valid": false - }, - { - "description": "an array is not a string", - "data": [], - "valid": false - }, - { - "description": "a boolean is not a string", - "data": true, - "valid": false - }, - { - "description": "null is not a string", - "data": null, - "valid": false - } - ] - }, - { - "description": "object type matches objects", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object" - }, - "tests": [ - { - "description": "an integer is not an object", - "data": 1, - "valid": false - }, - { - "description": "a float is not an object", - "data": 1.1, - "valid": false - }, - { - "description": "a string is not an object", - "data": "foo", - "valid": false - }, - { - "description": "an object is an object", - "data": {}, - "valid": true - }, - { - "description": "an array is not an object", - "data": [], - "valid": false - }, - { - "description": "a boolean is not an object", - "data": true, - "valid": false - }, - { - "description": "null is not an object", - "data": null, - "valid": false - } - ] - }, - { - "description": "array type matches arrays", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "array" - }, - "tests": [ - { - "description": "an integer is not an array", - "data": 1, - "valid": false - }, - { - "description": "a float is not an array", - "data": 1.1, - "valid": false - }, - { - "description": "a string is not an array", - "data": "foo", - "valid": false - }, - { - "description": "an object is not an array", - "data": {}, - "valid": false - }, - { - "description": "an array is an array", - "data": [], - "valid": true - }, - { - "description": "a boolean is not an array", - "data": true, - "valid": false - }, - { - "description": "null is not an array", - "data": null, - "valid": false - } - ] - }, - { - "description": "boolean type matches booleans", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "boolean" - }, - "tests": [ - { - "description": "an integer is not a boolean", - "data": 1, - "valid": false - }, - { - "description": "zero is not a boolean", - "data": 0, - "valid": false - }, - { - "description": "a float is not a boolean", - "data": 1.1, - "valid": false - }, - { - "description": "a string is not a boolean", - "data": "foo", - "valid": false - }, - { - "description": "an empty string is not a boolean", - "data": "", - "valid": false - }, - { - "description": "an object is not a boolean", - "data": {}, - "valid": false - }, - { - "description": "an array is not a boolean", - "data": [], - "valid": false - }, - { - "description": "true is a boolean", - "data": true, - "valid": true - }, - { - "description": "false is a boolean", - "data": false, - "valid": true - }, - { - "description": "null is not a boolean", - "data": null, - "valid": false - } - ] - }, - { - "description": "null type matches only the null object", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "null" - }, - "tests": [ - { - "description": "an integer is not null", - "data": 1, - "valid": false - }, - { - "description": "a float is not null", - "data": 1.1, - "valid": false - }, - { - "description": "zero is not null", - "data": 0, - "valid": false - }, - { - "description": "a string is not null", - "data": "foo", - "valid": false - }, - { - "description": "an empty string is not null", - "data": "", - "valid": false - }, - { - "description": "an object is not null", - "data": {}, - "valid": false - }, - { - "description": "an array is not null", - "data": [], - "valid": false - }, - { - "description": "true is not null", - "data": true, - "valid": false - }, - { - "description": "false is not null", - "data": false, - "valid": false - }, - { - "description": "null is null", - "data": null, - "valid": true - } - ] - }, - { - "description": "multiple types can be specified in an array", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": ["integer", "string"] - }, - "tests": [ - { - "description": "an integer is valid", - "data": 1, - "valid": true - }, - { - "description": "a string is valid", - "data": "foo", - "valid": true - }, - { - "description": "a float is invalid", - "data": 1.1, - "valid": false - }, - { - "description": "an object is invalid", - "data": {}, - "valid": false - }, - { - "description": "an array is invalid", - "data": [], - "valid": false - }, - { - "description": "a boolean is invalid", - "data": true, - "valid": false - }, - { - "description": "null is invalid", - "data": null, - "valid": false - } - ] - }, - { - "description": "type as array with one item", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": ["string"] - }, - "tests": [ - { - "description": "string is valid", - "data": "foo", - "valid": true - }, - { - "description": "number is invalid", - "data": 123, - "valid": false - } - ] - }, - { - "description": "type: array or object", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": ["array", "object"] - }, - "tests": [ - { - "description": "array is valid", - "data": [1,2,3], - "valid": true - }, - { - "description": "object is valid", - "data": {"foo": 123}, - "valid": true - }, - { - "description": "number is invalid", - "data": 123, - "valid": false - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - }, - { - "description": "null is invalid", - "data": null, - "valid": false - } - ] - }, - { - "description": "type: array, object or null", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": ["array", "object", "null"] - }, - "tests": [ - { - "description": "array is valid", - "data": [1,2,3], - "valid": true - }, - { - "description": "object is valid", - "data": {"foo": 123}, - "valid": true - }, - { - "description": "null is valid", - "data": null, - "valid": true - }, - { - "description": "number is invalid", - "data": 123, - "valid": false - }, - { - "description": "string is invalid", - "data": "foo", - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/unevaluatedItems.json b/jsonschema/testdata/draft2020-12/unevaluatedItems.json deleted file mode 100644 index f861cefa..00000000 --- a/jsonschema/testdata/draft2020-12/unevaluatedItems.json +++ /dev/null @@ -1,798 +0,0 @@ -[ - { - "description": "unevaluatedItems true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": true - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": [], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo"], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": [], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems as schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": { "type": "string" } - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": [], - "valid": true - }, - { - "description": "with valid unevaluated items", - "data": ["foo"], - "valid": true - }, - { - "description": "with invalid unevaluated items", - "data": [42], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with uniform items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": { "type": "string" }, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "unevaluatedItems doesn't apply", - "data": ["foo", "bar"], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems with tuple", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "type": "string" } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo"], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", "bar"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with items and prefixItems", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "type": "string" } - ], - "items": true, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "unevaluatedItems doesn't apply", - "data": ["foo", 42], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems with items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "items": {"type": "number"}, - "unevaluatedItems": {"type": "string"} - }, - "tests": [ - { - "description": "valid under items", - "comment": "no elements are considered by unevaluatedItems", - "data": [5, 6, 7, 8], - "valid": true - }, - { - "description": "invalid under items", - "data": ["foo", "bar", "baz"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with nested tuple", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "type": "string" } - ], - "allOf": [ - { - "prefixItems": [ - true, - { "type": "number" } - ] - } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo", 42], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", 42, true], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with nested items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": {"type": "boolean"}, - "anyOf": [ - { "items": {"type": "string"} }, - true - ] - }, - "tests": [ - { - "description": "with only (valid) additional items", - "data": [true, false], - "valid": true - }, - { - "description": "with no additional items", - "data": ["yes", "no"], - "valid": true - }, - { - "description": "with invalid additional item", - "data": ["yes", false], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with nested prefixItems and items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "prefixItems": [ - { "type": "string" } - ], - "items": true - } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no additional items", - "data": ["foo"], - "valid": true - }, - { - "description": "with additional items", - "data": ["foo", 42, true], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems with nested unevaluatedItems", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "prefixItems": [ - { "type": "string" } - ] - }, - { "unevaluatedItems": true } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no additional items", - "data": ["foo"], - "valid": true - }, - { - "description": "with additional items", - "data": ["foo", 42, true], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems with anyOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "const": "foo" } - ], - "anyOf": [ - { - "prefixItems": [ - true, - { "const": "bar" } - ] - }, - { - "prefixItems": [ - true, - true, - { "const": "baz" } - ] - } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "when one schema matches and has no unevaluated items", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "when one schema matches and has unevaluated items", - "data": ["foo", "bar", 42], - "valid": false - }, - { - "description": "when two schemas match and has no unevaluated items", - "data": ["foo", "bar", "baz"], - "valid": true - }, - { - "description": "when two schemas match and has unevaluated items", - "data": ["foo", "bar", "baz", 42], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with oneOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "const": "foo" } - ], - "oneOf": [ - { - "prefixItems": [ - true, - { "const": "bar" } - ] - }, - { - "prefixItems": [ - true, - { "const": "baz" } - ] - } - ], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", "bar", 42], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with not", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "const": "foo" } - ], - "not": { - "not": { - "prefixItems": [ - true, - { "const": "bar" } - ] - } - }, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with unevaluated items", - "data": ["foo", "bar"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with if/then/else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - { "const": "foo" } - ], - "if": { - "prefixItems": [ - true, - { "const": "bar" } - ] - }, - "then": { - "prefixItems": [ - true, - true, - { "const": "then" } - ] - }, - "else": { - "prefixItems": [ - true, - true, - true, - { "const": "else" } - ] - }, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "when if matches and it has no unevaluated items", - "data": ["foo", "bar", "then"], - "valid": true - }, - { - "description": "when if matches and it has unevaluated items", - "data": ["foo", "bar", "then", "else"], - "valid": false - }, - { - "description": "when if doesn't match and it has no unevaluated items", - "data": ["foo", 42, 42, "else"], - "valid": true - }, - { - "description": "when if doesn't match and it has unevaluated items", - "data": ["foo", 42, 42, "else", 42], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with boolean schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [true], - "unevaluatedItems": false - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": [], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$ref": "#/$defs/bar", - "prefixItems": [ - { "type": "string" } - ], - "unevaluatedItems": false, - "$defs": { - "bar": { - "prefixItems": [ - true, - { "type": "string" } - ] - } - } - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", "bar", "baz"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems before $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": false, - "prefixItems": [ - { "type": "string" } - ], - "$ref": "#/$defs/bar", - "$defs": { - "bar": { - "prefixItems": [ - true, - { "type": "string" } - ] - } - } - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", "bar", "baz"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems with $dynamicRef", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/unevaluated-items-with-dynamic-ref/derived", - - "$ref": "./baseSchema", - - "$defs": { - "derived": { - "$dynamicAnchor": "addons", - "prefixItems": [ - true, - { "type": "string" } - ] - }, - "baseSchema": { - "$id": "./baseSchema", - - "$comment": "unevaluatedItems comes first so it's more likely to catch bugs with implementations that are sensitive to keyword ordering", - "unevaluatedItems": false, - "type": "array", - "prefixItems": [ - { "type": "string" } - ], - "$dynamicRef": "#addons", - - "$defs": { - "defaultAddons": { - "$comment": "Needed to satisfy the bookending requirement", - "$dynamicAnchor": "addons" - } - } - } - } - }, - "tests": [ - { - "description": "with no unevaluated items", - "data": ["foo", "bar"], - "valid": true - }, - { - "description": "with unevaluated items", - "data": ["foo", "bar", "baz"], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems can't see inside cousins", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "prefixItems": [ true ] - }, - { "unevaluatedItems": false } - ] - }, - "tests": [ - { - "description": "always fails", - "data": [ 1 ], - "valid": false - } - ] - }, - { - "description": "item is evaluated in an uncle schema to unevaluatedItems", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": { - "foo": { - "prefixItems": [ - { "type": "string" } - ], - "unevaluatedItems": false - } - }, - "anyOf": [ - { - "properties": { - "foo": { - "prefixItems": [ - true, - { "type": "string" } - ] - } - } - } - ] - }, - "tests": [ - { - "description": "no extra items", - "data": { - "foo": [ - "test" - ] - }, - "valid": true - }, - { - "description": "uncle keyword evaluation is not significant", - "data": { - "foo": [ - "test", - "test" - ] - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedItems depends on adjacent contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [true], - "contains": {"type": "string"}, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "second item is evaluated by contains", - "data": [ 1, "foo" ], - "valid": true - }, - { - "description": "contains fails, second item is not evaluated", - "data": [ 1, 2 ], - "valid": false - }, - { - "description": "contains passes, second item is not evaluated", - "data": [ 1, 2, "foo" ], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems depends on multiple nested contains", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { "contains": { "multipleOf": 2 } }, - { "contains": { "multipleOf": 3 } } - ], - "unevaluatedItems": { "multipleOf": 5 } - }, - "tests": [ - { - "description": "5 not evaluated, passes unevaluatedItems", - "data": [ 2, 3, 4, 5, 6 ], - "valid": true - }, - { - "description": "7 not evaluated, fails unevaluatedItems", - "data": [ 2, 3, 4, 7, 8 ], - "valid": false - } - ] - }, - { - "description": "unevaluatedItems and contains interact to control item dependency relationship", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "contains": {"const": "a"} - }, - "then": { - "if": { - "contains": {"const": "b"} - }, - "then": { - "if": { - "contains": {"const": "c"} - } - } - }, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "empty array is valid", - "data": [], - "valid": true - }, - { - "description": "only a's are valid", - "data": [ "a", "a" ], - "valid": true - }, - { - "description": "a's and b's are valid", - "data": [ "a", "b", "a", "b", "a" ], - "valid": true - }, - { - "description": "a's, b's and c's are valid", - "data": [ "c", "a", "c", "c", "b", "a" ], - "valid": true - }, - { - "description": "only b's are invalid", - "data": [ "b", "b" ], - "valid": false - }, - { - "description": "only c's are invalid", - "data": [ "c", "c" ], - "valid": false - }, - { - "description": "only b's and c's are invalid", - "data": [ "c", "b", "c", "b", "c" ], - "valid": false - }, - { - "description": "only a's and c's are invalid", - "data": [ "c", "a", "c", "a", "c" ], - "valid": false - } - ] - }, - { - "description": "non-array instances are valid", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": false - }, - "tests": [ - { - "description": "ignores booleans", - "data": true, - "valid": true - }, - { - "description": "ignores integers", - "data": 123, - "valid": true - }, - { - "description": "ignores floats", - "data": 1.0, - "valid": true - }, - { - "description": "ignores objects", - "data": {}, - "valid": true - }, - { - "description": "ignores strings", - "data": "foo", - "valid": true - }, - { - "description": "ignores null", - "data": null, - "valid": true - } - ] - }, - { - "description": "unevaluatedItems with null instance elements", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedItems": { - "type": "null" - } - }, - "tests": [ - { - "description": "allows null elements", - "data": [ null ], - "valid": true - } - ] - }, - { - "description": "unevaluatedItems can see annotations from if without then and else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "prefixItems": [{"const": "a"}] - }, - "unevaluatedItems": false - }, - "tests": [ - { - "description": "valid in case if is evaluated", - "data": [ "a" ], - "valid": true - }, - { - "description": "invalid in case if is evaluated", - "data": [ "b" ], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/unevaluatedProperties.json b/jsonschema/testdata/draft2020-12/unevaluatedProperties.json deleted file mode 100644 index ae29c9eb..00000000 --- a/jsonschema/testdata/draft2020-12/unevaluatedProperties.json +++ /dev/null @@ -1,1601 +0,0 @@ -[ - { - "description": "unevaluatedProperties true", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "unevaluatedProperties": true - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": {}, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties schema", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "unevaluatedProperties": { - "type": "string", - "minLength": 3 - } - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": {}, - "valid": true - }, - { - "description": "with valid unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with invalid unevaluated properties", - "data": { - "foo": "fo" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": {}, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with adjacent properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with adjacent patternProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "patternProperties": { - "^foo": { "type": "string" } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with adjacent additionalProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "additionalProperties": true, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no additional properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with additional properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties with nested properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "properties": { - "bar": { "type": "string" } - } - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no additional properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with additional properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with nested patternProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "patternProperties": { - "^bar": { "type": "string" } - } - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no additional properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with additional properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with nested additionalProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "additionalProperties": true - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no additional properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with additional properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties with nested unevaluatedProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "unevaluatedProperties": true - } - ], - "unevaluatedProperties": { - "type": "string", - "maxLength": 2 - } - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties with anyOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "anyOf": [ - { - "properties": { - "bar": { "const": "bar" } - }, - "required": ["bar"] - }, - { - "properties": { - "baz": { "const": "baz" } - }, - "required": ["baz"] - }, - { - "properties": { - "quux": { "const": "quux" } - }, - "required": ["quux"] - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "when one matches and has no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "when one matches and has unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "not-baz" - }, - "valid": false - }, - { - "description": "when two match and has no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": true - }, - { - "description": "when two match and has unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz", - "quux": "not-quux" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with oneOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "oneOf": [ - { - "properties": { - "bar": { "const": "bar" } - }, - "required": ["bar"] - }, - { - "properties": { - "baz": { "const": "baz" } - }, - "required": ["baz"] - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "quux": "quux" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with not", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "not": { - "not": { - "properties": { - "bar": { "const": "bar" } - }, - "required": ["bar"] - } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with if/then/else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "if": { - "properties": { - "foo": { "const": "then" } - }, - "required": ["foo"] - }, - "then": { - "properties": { - "bar": { "type": "string" } - }, - "required": ["bar"] - }, - "else": { - "properties": { - "baz": { "type": "string" } - }, - "required": ["baz"] - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "when if is true and has no unevaluated properties", - "data": { - "foo": "then", - "bar": "bar" - }, - "valid": true - }, - { - "description": "when if is true and has unevaluated properties", - "data": { - "foo": "then", - "bar": "bar", - "baz": "baz" - }, - "valid": false - }, - { - "description": "when if is false and has no unevaluated properties", - "data": { - "baz": "baz" - }, - "valid": true - }, - { - "description": "when if is false and has unevaluated properties", - "data": { - "foo": "else", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with if/then/else, then not defined", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "if": { - "properties": { - "foo": { "const": "then" } - }, - "required": ["foo"] - }, - "else": { - "properties": { - "baz": { "type": "string" } - }, - "required": ["baz"] - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "when if is true and has no unevaluated properties", - "data": { - "foo": "then", - "bar": "bar" - }, - "valid": false - }, - { - "description": "when if is true and has unevaluated properties", - "data": { - "foo": "then", - "bar": "bar", - "baz": "baz" - }, - "valid": false - }, - { - "description": "when if is false and has no unevaluated properties", - "data": { - "baz": "baz" - }, - "valid": true - }, - { - "description": "when if is false and has unevaluated properties", - "data": { - "foo": "else", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with if/then/else, else not defined", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "if": { - "properties": { - "foo": { "const": "then" } - }, - "required": ["foo"] - }, - "then": { - "properties": { - "bar": { "type": "string" } - }, - "required": ["bar"] - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "when if is true and has no unevaluated properties", - "data": { - "foo": "then", - "bar": "bar" - }, - "valid": true - }, - { - "description": "when if is true and has unevaluated properties", - "data": { - "foo": "then", - "bar": "bar", - "baz": "baz" - }, - "valid": false - }, - { - "description": "when if is false and has no unevaluated properties", - "data": { - "baz": "baz" - }, - "valid": false - }, - { - "description": "when if is false and has unevaluated properties", - "data": { - "foo": "else", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with dependentSchemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "dependentSchemas": { - "foo": { - "properties": { - "bar": { "const": "bar" } - }, - "required": ["bar"] - } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with boolean schemas", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [true], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "$ref": "#/$defs/bar", - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": false, - "$defs": { - "bar": { - "properties": { - "bar": { "type": "string" } - } - } - } - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties before $ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "unevaluatedProperties": false, - "properties": { - "foo": { "type": "string" } - }, - "$ref": "#/$defs/bar", - "$defs": { - "bar": { - "properties": { - "bar": { "type": "string" } - } - } - } - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties with $dynamicRef", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/unevaluated-properties-with-dynamic-ref/derived", - - "$ref": "./baseSchema", - - "$defs": { - "derived": { - "$dynamicAnchor": "addons", - "properties": { - "bar": { "type": "string" } - } - }, - "baseSchema": { - "$id": "./baseSchema", - - "$comment": "unevaluatedProperties comes first so it's more likely to catch bugs with implementations that are sensitive to keyword ordering", - "unevaluatedProperties": false, - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "$dynamicRef": "#addons", - - "$defs": { - "defaultAddons": { - "$comment": "Needed to satisfy the bookending requirement", - "$dynamicAnchor": "addons" - } - } - } - } - }, - "tests": [ - { - "description": "with no unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - }, - { - "description": "with unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar", - "baz": "baz" - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties can't see inside cousins", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "properties": { - "foo": true - } - }, - { - "unevaluatedProperties": false - } - ] - }, - "tests": [ - { - "description": "always fails", - "data": { - "foo": 1 - }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties can't see inside cousins (reverse order)", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "allOf": [ - { - "unevaluatedProperties": false - }, - { - "properties": { - "foo": true - } - } - ] - }, - "tests": [ - { - "description": "always fails", - "data": { - "foo": 1 - }, - "valid": false - } - ] - }, - { - "description": "nested unevaluatedProperties, outer false, inner true, properties outside", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "unevaluatedProperties": true - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - } - ] - }, - { - "description": "nested unevaluatedProperties, outer false, inner true, properties inside", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": true - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": true - } - ] - }, - { - "description": "nested unevaluatedProperties, outer true, inner false, properties outside", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { "type": "string" } - }, - "allOf": [ - { - "unevaluatedProperties": false - } - ], - "unevaluatedProperties": true - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": false - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "nested unevaluatedProperties, outer true, inner false, properties inside", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": false - } - ], - "unevaluatedProperties": true - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "cousin unevaluatedProperties, true and false, true with properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": true - }, - { - "unevaluatedProperties": false - } - ] - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": false - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "cousin unevaluatedProperties, true and false, false with properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "unevaluatedProperties": true - }, - { - "properties": { - "foo": { "type": "string" } - }, - "unevaluatedProperties": false - } - ] - }, - "tests": [ - { - "description": "with no nested unevaluated properties", - "data": { - "foo": "foo" - }, - "valid": true - }, - { - "description": "with nested unevaluated properties", - "data": { - "foo": "foo", - "bar": "bar" - }, - "valid": false - } - ] - }, - { - "description": "property is evaluated in an uncle schema to unevaluatedProperties", - "comment": "see https://stackoverflow.com/questions/66936884/deeply-nested-unevaluatedproperties-and-their-expectations", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": { - "type": "object", - "properties": { - "bar": { - "type": "string" - } - }, - "unevaluatedProperties": false - } - }, - "anyOf": [ - { - "properties": { - "foo": { - "properties": { - "faz": { - "type": "string" - } - } - } - } - } - ] - }, - "tests": [ - { - "description": "no extra properties", - "data": { - "foo": { - "bar": "test" - } - }, - "valid": true - }, - { - "description": "uncle keyword evaluation is not significant", - "data": { - "foo": { - "bar": "test", - "faz": "test" - } - }, - "valid": false - } - ] - }, - { - "description": "in-place applicator siblings, allOf has unevaluated", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "properties": { - "foo": true - }, - "unevaluatedProperties": false - } - ], - "anyOf": [ - { - "properties": { - "bar": true - } - } - ] - }, - "tests": [ - { - "description": "base case: both properties present", - "data": { - "foo": 1, - "bar": 1 - }, - "valid": false - }, - { - "description": "in place applicator siblings, bar is missing", - "data": { - "foo": 1 - }, - "valid": true - }, - { - "description": "in place applicator siblings, foo is missing", - "data": { - "bar": 1 - }, - "valid": false - } - ] - }, - { - "description": "in-place applicator siblings, anyOf has unevaluated", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "allOf": [ - { - "properties": { - "foo": true - } - } - ], - "anyOf": [ - { - "properties": { - "bar": true - }, - "unevaluatedProperties": false - } - ] - }, - "tests": [ - { - "description": "base case: both properties present", - "data": { - "foo": 1, - "bar": 1 - }, - "valid": false - }, - { - "description": "in place applicator siblings, bar is missing", - "data": { - "foo": 1 - }, - "valid": false - }, - { - "description": "in place applicator siblings, foo is missing", - "data": { - "bar": 1 - }, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties + single cyclic ref", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "x": { "$ref": "#" } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "Empty is valid", - "data": {}, - "valid": true - }, - { - "description": "Single is valid", - "data": { "x": {} }, - "valid": true - }, - { - "description": "Unevaluated on 1st level is invalid", - "data": { "x": {}, "y": {} }, - "valid": false - }, - { - "description": "Nested is valid", - "data": { "x": { "x": {} } }, - "valid": true - }, - { - "description": "Unevaluated on 2nd level is invalid", - "data": { "x": { "x": {}, "y": {} } }, - "valid": false - }, - { - "description": "Deep nested is valid", - "data": { "x": { "x": { "x": {} } } }, - "valid": true - }, - { - "description": "Unevaluated on 3rd level is invalid", - "data": { "x": { "x": { "x": {}, "y": {} } } }, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties + ref inside allOf / oneOf", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "one": { - "properties": { "a": true } - }, - "two": { - "required": ["x"], - "properties": { "x": true } - } - }, - "allOf": [ - { "$ref": "#/$defs/one" }, - { "properties": { "b": true } }, - { - "oneOf": [ - { "$ref": "#/$defs/two" }, - { - "required": ["y"], - "properties": { "y": true } - } - ] - } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "Empty is invalid (no x or y)", - "data": {}, - "valid": false - }, - { - "description": "a and b are invalid (no x or y)", - "data": { "a": 1, "b": 1 }, - "valid": false - }, - { - "description": "x and y are invalid", - "data": { "x": 1, "y": 1 }, - "valid": false - }, - { - "description": "a and x are valid", - "data": { "a": 1, "x": 1 }, - "valid": true - }, - { - "description": "a and y are valid", - "data": { "a": 1, "y": 1 }, - "valid": true - }, - { - "description": "a and b and x are valid", - "data": { "a": 1, "b": 1, "x": 1 }, - "valid": true - }, - { - "description": "a and b and y are valid", - "data": { "a": 1, "b": 1, "y": 1 }, - "valid": true - }, - { - "description": "a and b and x and y are invalid", - "data": { "a": 1, "b": 1, "x": 1, "y": 1 }, - "valid": false - } - ] - }, - { - "description": "dynamic evalation inside nested refs", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "one": { - "oneOf": [ - { "$ref": "#/$defs/two" }, - { "required": ["b"], "properties": { "b": true } }, - { "required": ["xx"], "patternProperties": { "x": true } }, - { "required": ["all"], "unevaluatedProperties": true } - ] - }, - "two": { - "oneOf": [ - { "required": ["c"], "properties": { "c": true } }, - { "required": ["d"], "properties": { "d": true } } - ] - } - }, - "oneOf": [ - { "$ref": "#/$defs/one" }, - { "required": ["a"], "properties": { "a": true } } - ], - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "Empty is invalid", - "data": {}, - "valid": false - }, - { - "description": "a is valid", - "data": { "a": 1 }, - "valid": true - }, - { - "description": "b is valid", - "data": { "b": 1 }, - "valid": true - }, - { - "description": "c is valid", - "data": { "c": 1 }, - "valid": true - }, - { - "description": "d is valid", - "data": { "d": 1 }, - "valid": true - }, - { - "description": "a + b is invalid", - "data": { "a": 1, "b": 1 }, - "valid": false - }, - { - "description": "a + c is invalid", - "data": { "a": 1, "c": 1 }, - "valid": false - }, - { - "description": "a + d is invalid", - "data": { "a": 1, "d": 1 }, - "valid": false - }, - { - "description": "b + c is invalid", - "data": { "b": 1, "c": 1 }, - "valid": false - }, - { - "description": "b + d is invalid", - "data": { "b": 1, "d": 1 }, - "valid": false - }, - { - "description": "c + d is invalid", - "data": { "c": 1, "d": 1 }, - "valid": false - }, - { - "description": "xx is valid", - "data": { "xx": 1 }, - "valid": true - }, - { - "description": "xx + foox is valid", - "data": { "xx": 1, "foox": 1 }, - "valid": true - }, - { - "description": "xx + foo is invalid", - "data": { "xx": 1, "foo": 1 }, - "valid": false - }, - { - "description": "xx + a is invalid", - "data": { "xx": 1, "a": 1 }, - "valid": false - }, - { - "description": "xx + b is invalid", - "data": { "xx": 1, "b": 1 }, - "valid": false - }, - { - "description": "xx + c is invalid", - "data": { "xx": 1, "c": 1 }, - "valid": false - }, - { - "description": "xx + d is invalid", - "data": { "xx": 1, "d": 1 }, - "valid": false - }, - { - "description": "all is valid", - "data": { "all": 1 }, - "valid": true - }, - { - "description": "all + foo is valid", - "data": { "all": 1, "foo": 1 }, - "valid": true - }, - { - "description": "all + a is invalid", - "data": { "all": 1, "a": 1 }, - "valid": false - } - ] - }, - { - "description": "non-object instances are valid", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "ignores booleans", - "data": true, - "valid": true - }, - { - "description": "ignores integers", - "data": 123, - "valid": true - }, - { - "description": "ignores floats", - "data": 1.0, - "valid": true - }, - { - "description": "ignores arrays", - "data": [], - "valid": true - }, - { - "description": "ignores strings", - "data": "foo", - "valid": true - }, - { - "description": "ignores null", - "data": null, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties with null valued instance properties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "unevaluatedProperties": { - "type": "null" - } - }, - "tests": [ - { - "description": "allows null valued properties", - "data": {"foo": null}, - "valid": true - } - ] - }, - { - "description": "unevaluatedProperties not affected by propertyNames", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "propertyNames": {"maxLength": 1}, - "unevaluatedProperties": { - "type": "number" - } - }, - "tests": [ - { - "description": "allows only number properties", - "data": {"a": 1}, - "valid": true - }, - { - "description": "string property is invalid", - "data": {"a": "b"}, - "valid": false - } - ] - }, - { - "description": "unevaluatedProperties can see annotations from if without then and else", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "if": { - "patternProperties": { - "foo": { - "type": "string" - } - } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "valid in case if is evaluated", - "data": { - "foo": "a" - }, - "valid": true - }, - { - "description": "invalid in case if is evaluated", - "data": { - "bar": "a" - }, - "valid": false - } - ] - }, - { - "description": "dependentSchemas with unevaluatedProperties", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "properties": {"foo2": {}}, - "dependentSchemas": { - "foo" : {}, - "foo2": { - "properties": { - "bar":{} - } - } - }, - "unevaluatedProperties": false - }, - "tests": [ - { - "description": "unevaluatedProperties doesn't consider dependentSchemas", - "data": {"foo": ""}, - "valid": false - }, - { - "description": "unevaluatedProperties doesn't see bar when foo2 is absent", - "data": {"bar": ""}, - "valid": false - }, - { - "description": "unevaluatedProperties sees bar when foo2 is present", - "data": { "foo2": "", "bar": ""}, - "valid": true - } - ] - } -] diff --git a/jsonschema/testdata/draft2020-12/uniqueItems.json b/jsonschema/testdata/draft2020-12/uniqueItems.json deleted file mode 100644 index 4ea3bf98..00000000 --- a/jsonschema/testdata/draft2020-12/uniqueItems.json +++ /dev/null @@ -1,419 +0,0 @@ -[ - { - "description": "uniqueItems validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "uniqueItems": true - }, - "tests": [ - { - "description": "unique array of integers is valid", - "data": [1, 2], - "valid": true - }, - { - "description": "non-unique array of integers is invalid", - "data": [1, 1], - "valid": false - }, - { - "description": "non-unique array of more than two integers is invalid", - "data": [1, 2, 1], - "valid": false - }, - { - "description": "numbers are unique if mathematically unequal", - "data": [1.0, 1.00, 1], - "valid": false - }, - { - "description": "false is not equal to zero", - "data": [0, false], - "valid": true - }, - { - "description": "true is not equal to one", - "data": [1, true], - "valid": true - }, - { - "description": "unique array of strings is valid", - "data": ["foo", "bar", "baz"], - "valid": true - }, - { - "description": "non-unique array of strings is invalid", - "data": ["foo", "bar", "foo"], - "valid": false - }, - { - "description": "unique array of objects is valid", - "data": [{"foo": "bar"}, {"foo": "baz"}], - "valid": true - }, - { - "description": "non-unique array of objects is invalid", - "data": [{"foo": "bar"}, {"foo": "bar"}], - "valid": false - }, - { - "description": "property order of array of objects is ignored", - "data": [{"foo": "bar", "bar": "foo"}, {"bar": "foo", "foo": "bar"}], - "valid": false - }, - { - "description": "unique array of nested objects is valid", - "data": [ - {"foo": {"bar" : {"baz" : true}}}, - {"foo": {"bar" : {"baz" : false}}} - ], - "valid": true - }, - { - "description": "non-unique array of nested objects is invalid", - "data": [ - {"foo": {"bar" : {"baz" : true}}}, - {"foo": {"bar" : {"baz" : true}}} - ], - "valid": false - }, - { - "description": "unique array of arrays is valid", - "data": [["foo"], ["bar"]], - "valid": true - }, - { - "description": "non-unique array of arrays is invalid", - "data": [["foo"], ["foo"]], - "valid": false - }, - { - "description": "non-unique array of more than two arrays is invalid", - "data": [["foo"], ["bar"], ["foo"]], - "valid": false - }, - { - "description": "1 and true are unique", - "data": [1, true], - "valid": true - }, - { - "description": "0 and false are unique", - "data": [0, false], - "valid": true - }, - { - "description": "[1] and [true] are unique", - "data": [[1], [true]], - "valid": true - }, - { - "description": "[0] and [false] are unique", - "data": [[0], [false]], - "valid": true - }, - { - "description": "nested [1] and [true] are unique", - "data": [[[1], "foo"], [[true], "foo"]], - "valid": true - }, - { - "description": "nested [0] and [false] are unique", - "data": [[[0], "foo"], [[false], "foo"]], - "valid": true - }, - { - "description": "unique heterogeneous types are valid", - "data": [{}, [1], true, null, 1, "{}"], - "valid": true - }, - { - "description": "non-unique heterogeneous types are invalid", - "data": [{}, [1], true, null, {}, 1], - "valid": false - }, - { - "description": "different objects are unique", - "data": [{"a": 1, "b": 2}, {"a": 2, "b": 1}], - "valid": true - }, - { - "description": "objects are non-unique despite key order", - "data": [{"a": 1, "b": 2}, {"b": 2, "a": 1}], - "valid": false - }, - { - "description": "{\"a\": false} and {\"a\": 0} are unique", - "data": [{"a": false}, {"a": 0}], - "valid": true - }, - { - "description": "{\"a\": true} and {\"a\": 1} are unique", - "data": [{"a": true}, {"a": 1}], - "valid": true - } - ] - }, - { - "description": "uniqueItems with an array of items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{"type": "boolean"}, {"type": "boolean"}], - "uniqueItems": true - }, - "tests": [ - { - "description": "[false, true] from items array is valid", - "data": [false, true], - "valid": true - }, - { - "description": "[true, false] from items array is valid", - "data": [true, false], - "valid": true - }, - { - "description": "[false, false] from items array is not valid", - "data": [false, false], - "valid": false - }, - { - "description": "[true, true] from items array is not valid", - "data": [true, true], - "valid": false - }, - { - "description": "unique array extended from [false, true] is valid", - "data": [false, true, "foo", "bar"], - "valid": true - }, - { - "description": "unique array extended from [true, false] is valid", - "data": [true, false, "foo", "bar"], - "valid": true - }, - { - "description": "non-unique array extended from [false, true] is not valid", - "data": [false, true, "foo", "foo"], - "valid": false - }, - { - "description": "non-unique array extended from [true, false] is not valid", - "data": [true, false, "foo", "foo"], - "valid": false - } - ] - }, - { - "description": "uniqueItems with an array of items and additionalItems=false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{"type": "boolean"}, {"type": "boolean"}], - "uniqueItems": true, - "items": false - }, - "tests": [ - { - "description": "[false, true] from items array is valid", - "data": [false, true], - "valid": true - }, - { - "description": "[true, false] from items array is valid", - "data": [true, false], - "valid": true - }, - { - "description": "[false, false] from items array is not valid", - "data": [false, false], - "valid": false - }, - { - "description": "[true, true] from items array is not valid", - "data": [true, true], - "valid": false - }, - { - "description": "extra items are invalid even if unique", - "data": [false, true, null], - "valid": false - } - ] - }, - { - "description": "uniqueItems=false validation", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "uniqueItems": false - }, - "tests": [ - { - "description": "unique array of integers is valid", - "data": [1, 2], - "valid": true - }, - { - "description": "non-unique array of integers is valid", - "data": [1, 1], - "valid": true - }, - { - "description": "numbers are unique if mathematically unequal", - "data": [1.0, 1.00, 1], - "valid": true - }, - { - "description": "false is not equal to zero", - "data": [0, false], - "valid": true - }, - { - "description": "true is not equal to one", - "data": [1, true], - "valid": true - }, - { - "description": "unique array of objects is valid", - "data": [{"foo": "bar"}, {"foo": "baz"}], - "valid": true - }, - { - "description": "non-unique array of objects is valid", - "data": [{"foo": "bar"}, {"foo": "bar"}], - "valid": true - }, - { - "description": "unique array of nested objects is valid", - "data": [ - {"foo": {"bar" : {"baz" : true}}}, - {"foo": {"bar" : {"baz" : false}}} - ], - "valid": true - }, - { - "description": "non-unique array of nested objects is valid", - "data": [ - {"foo": {"bar" : {"baz" : true}}}, - {"foo": {"bar" : {"baz" : true}}} - ], - "valid": true - }, - { - "description": "unique array of arrays is valid", - "data": [["foo"], ["bar"]], - "valid": true - }, - { - "description": "non-unique array of arrays is valid", - "data": [["foo"], ["foo"]], - "valid": true - }, - { - "description": "1 and true are unique", - "data": [1, true], - "valid": true - }, - { - "description": "0 and false are unique", - "data": [0, false], - "valid": true - }, - { - "description": "unique heterogeneous types are valid", - "data": [{}, [1], true, null, 1], - "valid": true - }, - { - "description": "non-unique heterogeneous types are valid", - "data": [{}, [1], true, null, {}, 1], - "valid": true - } - ] - }, - { - "description": "uniqueItems=false with an array of items", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{"type": "boolean"}, {"type": "boolean"}], - "uniqueItems": false - }, - "tests": [ - { - "description": "[false, true] from items array is valid", - "data": [false, true], - "valid": true - }, - { - "description": "[true, false] from items array is valid", - "data": [true, false], - "valid": true - }, - { - "description": "[false, false] from items array is valid", - "data": [false, false], - "valid": true - }, - { - "description": "[true, true] from items array is valid", - "data": [true, true], - "valid": true - }, - { - "description": "unique array extended from [false, true] is valid", - "data": [false, true, "foo", "bar"], - "valid": true - }, - { - "description": "unique array extended from [true, false] is valid", - "data": [true, false, "foo", "bar"], - "valid": true - }, - { - "description": "non-unique array extended from [false, true] is valid", - "data": [false, true, "foo", "foo"], - "valid": true - }, - { - "description": "non-unique array extended from [true, false] is valid", - "data": [true, false, "foo", "foo"], - "valid": true - } - ] - }, - { - "description": "uniqueItems=false with an array of items and additionalItems=false", - "schema": { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [{"type": "boolean"}, {"type": "boolean"}], - "uniqueItems": false, - "items": false - }, - "tests": [ - { - "description": "[false, true] from items array is valid", - "data": [false, true], - "valid": true - }, - { - "description": "[true, false] from items array is valid", - "data": [true, false], - "valid": true - }, - { - "description": "[false, false] from items array is valid", - "data": [false, false], - "valid": true - }, - { - "description": "[true, true] from items array is valid", - "data": [true, true], - "valid": true - }, - { - "description": "extra items are invalid even if unique", - "data": [false, true, null], - "valid": false - } - ] - } -] diff --git a/jsonschema/testdata/remotes/README.md b/jsonschema/testdata/remotes/README.md deleted file mode 100644 index 8a641dbd..00000000 --- a/jsonschema/testdata/remotes/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# JSON Schema test suite: remote references - -These files were copied from -https://github.com/json-schema-org/JSON-Schema-Test-Suite/tree/83e866b46c9f9e7082fd51e83a61c5f2145a1ab7/remotes. diff --git a/jsonschema/testdata/remotes/different-id-ref-string.json b/jsonschema/testdata/remotes/different-id-ref-string.json deleted file mode 100644 index 7f888609..00000000 --- a/jsonschema/testdata/remotes/different-id-ref-string.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$id": "http://localhost:1234/real-id-ref-string.json", - "$defs": {"bar": {"type": "string"}}, - "$ref": "#/$defs/bar" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/baseUriChange/folderInteger.json b/jsonschema/testdata/remotes/draft2020-12/baseUriChange/folderInteger.json deleted file mode 100644 index 1f44a631..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/baseUriChange/folderInteger.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolder/folderInteger.json b/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolder/folderInteger.json deleted file mode 100644 index 1f44a631..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolder/folderInteger.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolderInSubschema/folderInteger.json b/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolderInSubschema/folderInteger.json deleted file mode 100644 index 1f44a631..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/baseUriChangeFolderInSubschema/folderInteger.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/detached-dynamicref.json b/jsonschema/testdata/remotes/draft2020-12/detached-dynamicref.json deleted file mode 100644 index 07cce1da..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/detached-dynamicref.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$id": "http://localhost:1234/draft2020-12/detached-dynamicref.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "foo": { - "$dynamicRef": "#detached" - }, - "detached": { - "$dynamicAnchor": "detached", - "type": "integer" - } - } -} \ No newline at end of file diff --git a/jsonschema/testdata/remotes/draft2020-12/detached-ref.json b/jsonschema/testdata/remotes/draft2020-12/detached-ref.json deleted file mode 100644 index 9c2dca93..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/detached-ref.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$id": "http://localhost:1234/draft2020-12/detached-ref.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "foo": { - "$ref": "#detached" - }, - "detached": { - "$anchor": "detached", - "type": "integer" - } - } -} \ No newline at end of file diff --git a/jsonschema/testdata/remotes/draft2020-12/extendible-dynamic-ref.json b/jsonschema/testdata/remotes/draft2020-12/extendible-dynamic-ref.json deleted file mode 100644 index 65bc0c21..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/extendible-dynamic-ref.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "description": "extendible array", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/extendible-dynamic-ref.json", - "type": "object", - "properties": { - "elements": { - "type": "array", - "items": { - "$dynamicRef": "#elements" - } - } - }, - "required": ["elements"], - "additionalProperties": false, - "$defs": { - "elements": { - "$dynamicAnchor": "elements" - } - } -} diff --git a/jsonschema/testdata/remotes/draft2020-12/format-assertion-false.json b/jsonschema/testdata/remotes/draft2020-12/format-assertion-false.json deleted file mode 100644 index 43a711c9..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/format-assertion-false.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$id": "http://localhost:1234/draft2020-12/format-assertion-false.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$vocabulary": { - "https://json-schema.org/draft/2020-12/vocab/core": true, - "https://json-schema.org/draft/2020-12/vocab/format-assertion": false - }, - "$dynamicAnchor": "meta", - "allOf": [ - { "$ref": "https://json-schema.org/draft/2020-12/meta/core" }, - { "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" } - ] -} diff --git a/jsonschema/testdata/remotes/draft2020-12/format-assertion-true.json b/jsonschema/testdata/remotes/draft2020-12/format-assertion-true.json deleted file mode 100644 index 39c6b0ab..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/format-assertion-true.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$id": "http://localhost:1234/draft2020-12/format-assertion-true.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$vocabulary": { - "https://json-schema.org/draft/2020-12/vocab/core": true, - "https://json-schema.org/draft/2020-12/vocab/format-assertion": true - }, - "$dynamicAnchor": "meta", - "allOf": [ - { "$ref": "https://json-schema.org/draft/2020-12/meta/core" }, - { "$ref": "https://json-schema.org/draft/2020-12/meta/format-assertion" } - ] -} diff --git a/jsonschema/testdata/remotes/draft2020-12/integer.json b/jsonschema/testdata/remotes/draft2020-12/integer.json deleted file mode 100644 index 1f44a631..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/integer.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "integer" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/locationIndependentIdentifier.json b/jsonschema/testdata/remotes/draft2020-12/locationIndependentIdentifier.json deleted file mode 100644 index 6565a1ee..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/locationIndependentIdentifier.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "refToInteger": { - "$ref": "#foo" - }, - "A": { - "$anchor": "foo", - "type": "integer" - } - } -} diff --git a/jsonschema/testdata/remotes/draft2020-12/metaschema-no-validation.json b/jsonschema/testdata/remotes/draft2020-12/metaschema-no-validation.json deleted file mode 100644 index 71be8b5d..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/metaschema-no-validation.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/metaschema-no-validation.json", - "$vocabulary": { - "https://json-schema.org/draft/2020-12/vocab/applicator": true, - "https://json-schema.org/draft/2020-12/vocab/core": true - }, - "$dynamicAnchor": "meta", - "allOf": [ - { "$ref": "https://json-schema.org/draft/2020-12/meta/applicator" }, - { "$ref": "https://json-schema.org/draft/2020-12/meta/core" } - ] -} diff --git a/jsonschema/testdata/remotes/draft2020-12/metaschema-optional-vocabulary.json b/jsonschema/testdata/remotes/draft2020-12/metaschema-optional-vocabulary.json deleted file mode 100644 index a6963e54..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/metaschema-optional-vocabulary.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/metaschema-optional-vocabulary.json", - "$vocabulary": { - "https://json-schema.org/draft/2020-12/vocab/validation": true, - "https://json-schema.org/draft/2020-12/vocab/core": true, - "http://localhost:1234/draft/2020-12/vocab/custom": false - }, - "$dynamicAnchor": "meta", - "allOf": [ - { "$ref": "https://json-schema.org/draft/2020-12/meta/validation" }, - { "$ref": "https://json-schema.org/draft/2020-12/meta/core" } - ] -} diff --git a/jsonschema/testdata/remotes/draft2020-12/name-defs.json b/jsonschema/testdata/remotes/draft2020-12/name-defs.json deleted file mode 100644 index 67bc33c5..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/name-defs.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "orNull": { - "anyOf": [ - { - "type": "null" - }, - { - "$ref": "#" - } - ] - } - }, - "type": "string" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/nested/foo-ref-string.json b/jsonschema/testdata/remotes/draft2020-12/nested/foo-ref-string.json deleted file mode 100644 index 29661ff9..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/nested/foo-ref-string.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "foo": {"$ref": "string.json"} - } -} diff --git a/jsonschema/testdata/remotes/draft2020-12/nested/string.json b/jsonschema/testdata/remotes/draft2020-12/nested/string.json deleted file mode 100644 index 6607ac53..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/nested/string.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "string" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/prefixItems.json b/jsonschema/testdata/remotes/draft2020-12/prefixItems.json deleted file mode 100644 index acd8293c..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/prefixItems.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "$id": "http://localhost:1234/draft2020-12/prefixItems.json", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "prefixItems": [ - {"type": "string"} - ] -} diff --git a/jsonschema/testdata/remotes/draft2020-12/ref-and-defs.json b/jsonschema/testdata/remotes/draft2020-12/ref-and-defs.json deleted file mode 100644 index 16d30fa3..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/ref-and-defs.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/ref-and-defs.json", - "$defs": { - "inner": { - "properties": { - "bar": { "type": "string" } - } - } - }, - "$ref": "#/$defs/inner" -} diff --git a/jsonschema/testdata/remotes/draft2020-12/subSchemas.json b/jsonschema/testdata/remotes/draft2020-12/subSchemas.json deleted file mode 100644 index 1bb4846d..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/subSchemas.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$defs": { - "integer": { - "type": "integer" - }, - "refToInteger": { - "$ref": "#/$defs/integer" - } - } -} diff --git a/jsonschema/testdata/remotes/draft2020-12/tree.json b/jsonschema/testdata/remotes/draft2020-12/tree.json deleted file mode 100644 index b07555fb..00000000 --- a/jsonschema/testdata/remotes/draft2020-12/tree.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "description": "tree schema, extensible", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "http://localhost:1234/draft2020-12/tree.json", - "$dynamicAnchor": "node", - - "type": "object", - "properties": { - "data": true, - "children": { - "type": "array", - "items": { - "$dynamicRef": "#node" - } - } - } -} diff --git a/jsonschema/testdata/remotes/nested-absolute-ref-to-string.json b/jsonschema/testdata/remotes/nested-absolute-ref-to-string.json deleted file mode 100644 index f46c7616..00000000 --- a/jsonschema/testdata/remotes/nested-absolute-ref-to-string.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$defs": { - "bar": { - "$id": "http://localhost:1234/the-nested-id.json", - "type": "string" - } - }, - "$ref": "http://localhost:1234/the-nested-id.json" -} diff --git a/jsonschema/testdata/remotes/urn-ref-string.json b/jsonschema/testdata/remotes/urn-ref-string.json deleted file mode 100644 index aca2211b..00000000 --- a/jsonschema/testdata/remotes/urn-ref-string.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$id": "urn:uuid:feebdaed-ffff-0000-ffff-0000deadbeef", - "$defs": {"bar": {"type": "string"}}, - "$ref": "#/$defs/bar" -} diff --git a/jsonschema/util.go b/jsonschema/util.go deleted file mode 100644 index 25b916cd..00000000 --- a/jsonschema/util.go +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "bytes" - "cmp" - "encoding/binary" - "encoding/json" - "fmt" - "hash/maphash" - "math" - "math/big" - "reflect" - "slices" - "strings" - "sync" -) - -// Equal reports whether two Go values representing JSON values are equal according -// to the JSON Schema spec. -// The values must not contain cycles. -// See https://json-schema.org/draft/2020-12/json-schema-core#section-4.2.2. -// It behaves like reflect.DeepEqual, except that numbers are compared according -// to mathematical equality. -func Equal(x, y any) bool { - return equalValue(reflect.ValueOf(x), reflect.ValueOf(y)) -} - -func equalValue(x, y reflect.Value) bool { - // Copied from src/reflect/deepequal.go, omitting the visited check (because JSON - // values are trees). - if !x.IsValid() || !y.IsValid() { - return x.IsValid() == y.IsValid() - } - - // Treat numbers specially. - rx, ok1 := jsonNumber(x) - ry, ok2 := jsonNumber(y) - if ok1 && ok2 { - return rx.Cmp(ry) == 0 - } - if x.Kind() != y.Kind() { - return false - } - switch x.Kind() { - case reflect.Array: - if x.Len() != y.Len() { - return false - } - for i := range x.Len() { - if !equalValue(x.Index(i), y.Index(i)) { - return false - } - } - return true - case reflect.Slice: - if x.IsNil() != y.IsNil() { - return false - } - if x.Len() != y.Len() { - return false - } - if x.UnsafePointer() == y.UnsafePointer() { - return true - } - // Special case for []byte, which is common. - if x.Type().Elem().Kind() == reflect.Uint8 && x.Type() == y.Type() { - return bytes.Equal(x.Bytes(), y.Bytes()) - } - for i := range x.Len() { - if !equalValue(x.Index(i), y.Index(i)) { - return false - } - } - return true - case reflect.Interface: - if x.IsNil() || y.IsNil() { - return x.IsNil() == y.IsNil() - } - return equalValue(x.Elem(), y.Elem()) - case reflect.Pointer: - if x.UnsafePointer() == y.UnsafePointer() { - return true - } - return equalValue(x.Elem(), y.Elem()) - case reflect.Struct: - t := x.Type() - if t != y.Type() { - return false - } - for i := range t.NumField() { - sf := t.Field(i) - if !sf.IsExported() { - continue - } - if !equalValue(x.FieldByIndex(sf.Index), y.FieldByIndex(sf.Index)) { - return false - } - } - return true - case reflect.Map: - if x.IsNil() != y.IsNil() { - return false - } - if x.Len() != y.Len() { - return false - } - if x.UnsafePointer() == y.UnsafePointer() { - return true - } - iter := x.MapRange() - for iter.Next() { - vx := iter.Value() - vy := y.MapIndex(iter.Key()) - if !vy.IsValid() || !equalValue(vx, vy) { - return false - } - } - return true - case reflect.Func: - if x.Type() != y.Type() { - return false - } - if x.IsNil() && y.IsNil() { - return true - } - panic("cannot compare functions") - case reflect.String: - return x.String() == y.String() - case reflect.Bool: - return x.Bool() == y.Bool() - // Ints, uints and floats handled in jsonNumber, at top of function. - default: - panic(fmt.Sprintf("unsupported kind: %s", x.Kind())) - } -} - -// hashValue adds v to the data hashed by h. v must not have cycles. -// hashValue panics if the value contains functions or channels, or maps whose -// key type is not string. -// It ignores unexported fields of structs. -// Calls to hashValue with the equal values (in the sense -// of [Equal]) result in the same sequence of values written to the hash. -func hashValue(h *maphash.Hash, v reflect.Value) { - // TODO: replace writes of basic types with WriteComparable in 1.24. - - writeUint := func(u uint64) { - var buf [8]byte - binary.BigEndian.PutUint64(buf[:], u) - h.Write(buf[:]) - } - - var write func(reflect.Value) - write = func(v reflect.Value) { - if r, ok := jsonNumber(v); ok { - // We want 1.0 and 1 to hash the same. - // big.Rats are always normalized, so they will be. - // We could do this more efficiently by handling the int and float cases - // separately, but that's premature. - writeUint(uint64(r.Sign() + 1)) - h.Write(r.Num().Bytes()) - h.Write(r.Denom().Bytes()) - return - } - switch v.Kind() { - case reflect.Invalid: - h.WriteByte(0) - case reflect.String: - h.WriteString(v.String()) - case reflect.Bool: - if v.Bool() { - h.WriteByte(1) - } else { - h.WriteByte(0) - } - case reflect.Complex64, reflect.Complex128: - c := v.Complex() - writeUint(math.Float64bits(real(c))) - writeUint(math.Float64bits(imag(c))) - case reflect.Array, reflect.Slice: - // Although we could treat []byte more efficiently, - // JSON values are unlikely to contain them. - writeUint(uint64(v.Len())) - for i := range v.Len() { - write(v.Index(i)) - } - case reflect.Interface, reflect.Pointer: - write(v.Elem()) - case reflect.Struct: - t := v.Type() - for i := range t.NumField() { - if sf := t.Field(i); sf.IsExported() { - write(v.FieldByIndex(sf.Index)) - } - } - case reflect.Map: - if v.Type().Key().Kind() != reflect.String { - panic("map with non-string key") - } - // Sort the keys so the hash is deterministic. - keys := v.MapKeys() - // Write the length. That distinguishes between, say, two consecutive - // maps with disjoint keys from one map that has the items of both. - writeUint(uint64(len(keys))) - slices.SortFunc(keys, func(x, y reflect.Value) int { return cmp.Compare(x.String(), y.String()) }) - for _, k := range keys { - write(k) - write(v.MapIndex(k)) - } - // Ints, uints and floats handled in jsonNumber, at top of function. - default: - panic(fmt.Sprintf("unsupported kind: %s", v.Kind())) - } - } - - write(v) -} - -// jsonNumber converts a numeric value or a json.Number to a [big.Rat]. -// If v is not a number, it returns nil, false. -func jsonNumber(v reflect.Value) (*big.Rat, bool) { - r := new(big.Rat) - switch { - case !v.IsValid(): - return nil, false - case v.CanInt(): - r.SetInt64(v.Int()) - case v.CanUint(): - r.SetUint64(v.Uint()) - case v.CanFloat(): - r.SetFloat64(v.Float()) - default: - jn, ok := v.Interface().(json.Number) - if !ok { - return nil, false - } - if _, ok := r.SetString(jn.String()); !ok { - // This can fail in rare cases; for example, "1e9999999". - // That is a valid JSON number, since the spec puts no limit on the size - // of the exponent. - return nil, false - } - } - return r, true -} - -// jsonType returns a string describing the type of the JSON value, -// as described in the JSON Schema specification: -// https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.1.1. -// It returns "", false if the value is not valid JSON. -func jsonType(v reflect.Value) (string, bool) { - if !v.IsValid() { - // Not v.IsNil(): a nil []any is still a JSON array. - return "null", true - } - if v.CanInt() || v.CanUint() { - return "integer", true - } - if v.CanFloat() { - if _, f := math.Modf(v.Float()); f == 0 { - return "integer", true - } - return "number", true - } - switch v.Kind() { - case reflect.Bool: - return "boolean", true - case reflect.String: - return "string", true - case reflect.Slice, reflect.Array: - return "array", true - case reflect.Map, reflect.Struct: - return "object", true - default: - return "", false - } -} - -func assert(cond bool, msg string) { - if !cond { - panic("assertion failed: " + msg) - } -} - -// marshalStructWithMap marshals its first argument to JSON, treating the field named -// mapField as an embedded map. The first argument must be a pointer to -// a struct. The underlying type of mapField must be a map[string]any, and it must have -// a "-" json tag, meaning it will not be marshaled. -// -// For example, given this struct: -// -// type S struct { -// A int -// Extra map[string] any `json:"-"` -// } -// -// and this value: -// -// s := S{A: 1, Extra: map[string]any{"B": 2}} -// -// the call marshalJSONWithMap(s, "Extra") would return -// -// {"A": 1, "B": 2} -// -// It is an error if the map contains the same key as another struct field's -// JSON name. -// -// marshalStructWithMap calls json.Marshal on a value of type T, so T must not -// have a MarshalJSON method that calls this function, on pain of infinite regress. -// -// Note that there is a similar function in mcp/util.go, but they are not the same. -// Here the function requires `-` json tag, does not clear the mapField map, -// and handles embedded struct due to the implementation of jsonNames in this package. -// -// TODO: avoid this restriction on T by forcing it to marshal in a default way. -// See https://go.dev/play/p/EgXKJHxEx_R. -func marshalStructWithMap[T any](s *T, mapField string) ([]byte, error) { - // Marshal the struct and the map separately, and concatenate the bytes. - // This strategy is dramatically less complicated than - // constructing a synthetic struct or map with the combined keys. - if s == nil { - return []byte("null"), nil - } - s2 := *s - vMapField := reflect.ValueOf(&s2).Elem().FieldByName(mapField) - mapVal := vMapField.Interface().(map[string]any) - - // Check for duplicates. - names := jsonNames(reflect.TypeFor[T]()) - for key := range mapVal { - if names[key] { - return nil, fmt.Errorf("map key %q duplicates struct field", key) - } - } - - structBytes, err := json.Marshal(s2) - if err != nil { - return nil, fmt.Errorf("marshalStructWithMap(%+v): %w", s, err) - } - if len(mapVal) == 0 { - return structBytes, nil - } - mapBytes, err := json.Marshal(mapVal) - if err != nil { - return nil, err - } - if len(structBytes) == 2 { // must be "{}" - return mapBytes, nil - } - // "{X}" + "{Y}" => "{X,Y}" - res := append(structBytes[:len(structBytes)-1], ',') - res = append(res, mapBytes[1:]...) - return res, nil -} - -// unmarshalStructWithMap is the inverse of marshalStructWithMap. -// T has the same restrictions as in that function. -// -// Note that there is a similar function in mcp/util.go, but they are not the same. -// Here jsonNames also returns fields from embedded structs, hence this function -// handles embedded structs as well. -func unmarshalStructWithMap[T any](data []byte, v *T, mapField string) error { - // Unmarshal into the struct, ignoring unknown fields. - if err := json.Unmarshal(data, v); err != nil { - return err - } - // Unmarshal into the map. - m := map[string]any{} - if err := json.Unmarshal(data, &m); err != nil { - return err - } - // Delete from the map the fields of the struct. - for n := range jsonNames(reflect.TypeFor[T]()) { - delete(m, n) - } - if len(m) != 0 { - reflect.ValueOf(v).Elem().FieldByName(mapField).Set(reflect.ValueOf(m)) - } - return nil -} - -var jsonNamesMap sync.Map // from reflect.Type to map[string]bool - -// jsonNames returns the set of JSON object keys that t will marshal into, -// including fields from embedded structs in t. -// t must be a struct type. -// -// Note that there is a similar function in mcp/util.go, but they are not the same -// Here the function recurses over embedded structs and includes fields from them. -func jsonNames(t reflect.Type) map[string]bool { - // Lock not necessary: at worst we'll duplicate work. - if val, ok := jsonNamesMap.Load(t); ok { - return val.(map[string]bool) - } - m := map[string]bool{} - for i := range t.NumField() { - field := t.Field(i) - // handle embedded structs - if field.Anonymous { - fieldType := field.Type - if fieldType.Kind() == reflect.Ptr { - fieldType = fieldType.Elem() - } - for n := range jsonNames(fieldType) { - m[n] = true - } - continue - } - info := fieldJSONInfo(field) - if !info.omit { - m[info.name] = true - } - } - jsonNamesMap.Store(t, m) - return m -} - -type jsonInfo struct { - omit bool // unexported or first tag element is "-" - name string // Go field name or first tag element. Empty if omit is true. - settings map[string]bool // "omitempty", "omitzero", etc. -} - -// fieldJSONInfo reports information about how encoding/json -// handles the given struct field. -// If the field is unexported, jsonInfo.omit is true and no other jsonInfo field -// is populated. -// If the field is exported and has no tag, then name is the field's name and all -// other fields are false. -// Otherwise, the information is obtained from the tag. -func fieldJSONInfo(f reflect.StructField) jsonInfo { - if !f.IsExported() { - return jsonInfo{omit: true} - } - info := jsonInfo{name: f.Name} - if tag, ok := f.Tag.Lookup("json"); ok { - name, rest, found := strings.Cut(tag, ",") - // "-" means omit, but "-," means the name is "-" - if name == "-" && !found { - return jsonInfo{omit: true} - } - if name != "" { - info.name = name - } - if len(rest) > 0 { - info.settings = map[string]bool{} - for _, s := range strings.Split(rest, ",") { - info.settings[s] = true - } - } - } - return info -} - -// wrapf wraps *errp with the given formatted message if *errp is not nil. -func wrapf(errp *error, format string, args ...any) { - if *errp != nil { - *errp = fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), *errp) - } -} diff --git a/jsonschema/util_test.go b/jsonschema/util_test.go deleted file mode 100644 index 7934bff7..00000000 --- a/jsonschema/util_test.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "encoding/json" - "hash/maphash" - "reflect" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func TestEqual(t *testing.T) { - for _, tt := range []struct { - x1, x2 any - want bool - }{ - {0, 1, false}, - {1, 1.0, true}, - {nil, 0, false}, - {"0", 0, false}, - {2.5, 2.5, true}, - {[]int{1, 2}, []float64{1.0, 2.0}, true}, - {[]int(nil), []int{}, false}, - {[]map[string]any(nil), []map[string]any{}, false}, - { - map[string]any{"a": 1, "b": 2.0}, - map[string]any{"a": 1.0, "b": 2}, - true, - }, - } { - check := func(x1, x2 any, want bool) { - t.Helper() - if got := Equal(x1, x2); got != want { - t.Errorf("jsonEqual(%#v, %#v) = %t, want %t", x1, x2, got, want) - } - } - check(tt.x1, tt.x1, true) - check(tt.x2, tt.x2, true) - check(tt.x1, tt.x2, tt.want) - check(tt.x2, tt.x1, tt.want) - } -} - -func TestJSONType(t *testing.T) { - for _, tt := range []struct { - val string - want string - }{ - {`null`, "null"}, - {`0`, "integer"}, - {`0.0`, "integer"}, - {`1e2`, "integer"}, - {`0.1`, "number"}, - {`""`, "string"}, - {`true`, "boolean"}, - {`[]`, "array"}, - {`{}`, "object"}, - } { - var val any - if err := json.Unmarshal([]byte(tt.val), &val); err != nil { - t.Fatal(err) - } - got, ok := jsonType(reflect.ValueOf(val)) - if !ok { - t.Fatalf("jsonType failed on %q", tt.val) - } - if got != tt.want { - t.Errorf("%s: got %q, want %q", tt.val, got, tt.want) - } - - } -} - -func TestHash(t *testing.T) { - x := map[string]any{ - "s": []any{1, "foo", nil, true}, - "f": 2.5, - "m": map[string]any{ - "n": json.Number("123.456"), - "schema": &Schema{Type: "integer", UniqueItems: true}, - }, - "c": 1.2 + 3.4i, - "n": nil, - } - - seed := maphash.MakeSeed() - - hash := func(x any) uint64 { - var h maphash.Hash - h.SetSeed(seed) - hashValue(&h, reflect.ValueOf(x)) - return h.Sum64() - } - - want := hash(x) - // Run several times to verify consistency. - for range 10 { - if got := hash(x); got != want { - t.Errorf("hash values differ: %d vs. %d", got, want) - } - } - - // Check mathematically equal values. - nums := []any{ - 5, - uint(5), - 5.0, - json.Number("5"), - json.Number("5.00"), - } - for i, n := range nums { - if i == 0 { - want = hash(n) - } else if got := hash(n); got != want { - t.Errorf("hashes differ between %v (%[1]T) and %v (%[2]T)", nums[0], n) - } - } - - // Check that a bare JSON `null` is OK. - var null any - if err := json.Unmarshal([]byte(`null`), &null); err != nil { - t.Fatal(err) - } - _ = hash(null) -} - -func TestMarshalStructWithMap(t *testing.T) { - type S struct { - A int - B string `json:"b,omitempty"` - u bool - M map[string]any `json:"-"` - } - t.Run("basic", func(t *testing.T) { - s := S{A: 1, B: "two", M: map[string]any{"!@#": true}} - got, err := marshalStructWithMap(&s, "M") - if err != nil { - t.Fatal(err) - } - want := `{"A":1,"b":"two","!@#":true}` - if g := string(got); g != want { - t.Errorf("\ngot %s\nwant %s", g, want) - } - - var un S - if err := unmarshalStructWithMap(got, &un, "M"); err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(s, un, cmpopts.IgnoreUnexported(S{})); diff != "" { - t.Errorf("mismatch (-want, +got):\n%s", diff) - } - }) - t.Run("duplicate", func(t *testing.T) { - s := S{A: 1, B: "two", M: map[string]any{"b": "dup"}} - _, err := marshalStructWithMap(&s, "M") - if err == nil || !strings.Contains(err.Error(), "duplicate") { - t.Errorf("got %v, want error with 'duplicate'", err) - } - }) - t.Run("embedded", func(t *testing.T) { - type Embedded struct { - A int - B int - Extra map[string]any `json:"-"` - } - type S struct { - C int - Embedded - } - s := S{C: 1, Embedded: Embedded{A: 2, B: 3, Extra: map[string]any{"d": 4, "e": 5}}} - got, err := marshalStructWithMap(&s, "Extra") - if err != nil { - t.Fatal(err) - } - want := `{"C":1,"A":2,"B":3,"d":4,"e":5}` - if g := string(got); g != want { - t.Errorf("got %v, want %v", g, want) - } - }) -} - -func TestJSONInfo(t *testing.T) { - type S struct { - A int - B int `json:","` - C int `json:"-"` - D int `json:"-,"` - E int `json:"echo"` - F int `json:"foxtrot,omitempty"` - g int `json:"golf"` - } - want := []jsonInfo{ - {name: "A"}, - {name: "B"}, - {omit: true}, - {name: "-"}, - {name: "echo"}, - {name: "foxtrot", settings: map[string]bool{"omitempty": true}}, - {omit: true}, - } - tt := reflect.TypeFor[S]() - for i := range tt.NumField() { - got := fieldJSONInfo(tt.Field(i)) - if !reflect.DeepEqual(got, want[i]) { - t.Errorf("got %+v, want %+v", got, want[i]) - } - } -} diff --git a/jsonschema/validate.go b/jsonschema/validate.go deleted file mode 100644 index 99ddd3b8..00000000 --- a/jsonschema/validate.go +++ /dev/null @@ -1,758 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "encoding/json" - "fmt" - "hash/maphash" - "iter" - "math" - "math/big" - "reflect" - "slices" - "strings" - "sync" - "unicode/utf8" -) - -// The value of the "$schema" keyword for the version that we can validate. -const draft202012 = "https://json-schema.org/draft/2020-12/schema" - -// Validate validates the instance, which must be a JSON value, against the schema. -// It returns nil if validation is successful or an error if it is not. -// If the schema type is "object", instance can be a map[string]any or a struct. -func (rs *Resolved) Validate(instance any) error { - if s := rs.root.Schema; s != "" && s != draft202012 { - return fmt.Errorf("cannot validate version %s, only %s", s, draft202012) - } - st := &state{rs: rs} - return st.validate(reflect.ValueOf(instance), st.rs.root, nil) -} - -// validateDefaults walks the schema tree. If it finds a default, it validates it -// against the schema containing it. -// -// TODO(jba): account for dynamic refs. This algorithm simple-mindedly -// treats each schema with a default as its own root. -func (rs *Resolved) validateDefaults() error { - if s := rs.root.Schema; s != "" && s != draft202012 { - return fmt.Errorf("cannot validate version %s, only %s", s, draft202012) - } - st := &state{rs: rs} - for s := range rs.root.all() { - // We checked for nil schemas in [Schema.Resolve]. - assert(s != nil, "nil schema") - if s.DynamicRef != "" { - return fmt.Errorf("jsonschema: %s: validateDefaults does not support dynamic refs", rs.schemaString(s)) - } - if s.Default != nil { - var d any - if err := json.Unmarshal(s.Default, &d); err != nil { - return fmt.Errorf("unmarshaling default value of schema %s: %w", rs.schemaString(s), err) - } - if err := st.validate(reflect.ValueOf(d), s, nil); err != nil { - return err - } - } - } - return nil -} - -// state is the state of single call to ResolvedSchema.Validate. -type state struct { - rs *Resolved - // stack holds the schemas from recursive calls to validate. - // These are the "dynamic scopes" used to resolve dynamic references. - // https://json-schema.org/draft/2020-12/json-schema-core#scopes - stack []*Schema -} - -// validate validates the reflected value of the instance. -func (st *state) validate(instance reflect.Value, schema *Schema, callerAnns *annotations) (err error) { - defer wrapf(&err, "validating %s", st.rs.schemaString(schema)) - - // Maintain a stack for dynamic schema resolution. - st.stack = append(st.stack, schema) // push - defer func() { - st.stack = st.stack[:len(st.stack)-1] // pop - }() - - // We checked for nil schemas in [Schema.Resolve]. - assert(schema != nil, "nil schema") - - // Step through interfaces and pointers. - for instance.Kind() == reflect.Pointer || instance.Kind() == reflect.Interface { - instance = instance.Elem() - } - - schemaInfo := st.rs.resolvedInfos[schema] - - // type: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.1.1 - if schema.Type != "" || schema.Types != nil { - gotType, ok := jsonType(instance) - if !ok { - return fmt.Errorf("type: %v of type %[1]T is not a valid JSON value", instance) - } - if schema.Type != "" { - // "number" subsumes integers - if !(gotType == schema.Type || - gotType == "integer" && schema.Type == "number") { - return fmt.Errorf("type: %v has type %q, want %q", instance, gotType, schema.Type) - } - } else { - if !(slices.Contains(schema.Types, gotType) || (gotType == "integer" && slices.Contains(schema.Types, "number"))) { - return fmt.Errorf("type: %v has type %q, want one of %q", - instance, gotType, strings.Join(schema.Types, ", ")) - } - } - } - // enum: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.1.2 - if schema.Enum != nil { - ok := false - for _, e := range schema.Enum { - if equalValue(reflect.ValueOf(e), instance) { - ok = true - break - } - } - if !ok { - return fmt.Errorf("enum: %v does not equal any of: %v", instance, schema.Enum) - } - } - - // const: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.1.3 - if schema.Const != nil { - if !equalValue(reflect.ValueOf(*schema.Const), instance) { - return fmt.Errorf("const: %v does not equal %v", instance, *schema.Const) - } - } - - // numbers: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.2 - if schema.MultipleOf != nil || schema.Minimum != nil || schema.Maximum != nil || schema.ExclusiveMinimum != nil || schema.ExclusiveMaximum != nil { - n, ok := jsonNumber(instance) - if ok { // these keywords don't apply to non-numbers - if schema.MultipleOf != nil { - // TODO: validate MultipleOf as non-zero. - // The test suite assumes floats. - nf, _ := n.Float64() // don't care if it's exact or not - if _, f := math.Modf(nf / *schema.MultipleOf); f != 0 { - return fmt.Errorf("multipleOf: %s is not a multiple of %f", n, *schema.MultipleOf) - } - } - - m := new(big.Rat) // reuse for all of the following - cmp := func(f float64) int { return n.Cmp(m.SetFloat64(f)) } - - if schema.Minimum != nil && cmp(*schema.Minimum) < 0 { - return fmt.Errorf("minimum: %s is less than %f", n, *schema.Minimum) - } - if schema.Maximum != nil && cmp(*schema.Maximum) > 0 { - return fmt.Errorf("maximum: %s is greater than %f", n, *schema.Maximum) - } - if schema.ExclusiveMinimum != nil && cmp(*schema.ExclusiveMinimum) <= 0 { - return fmt.Errorf("exclusiveMinimum: %s is less than or equal to %f", n, *schema.ExclusiveMinimum) - } - if schema.ExclusiveMaximum != nil && cmp(*schema.ExclusiveMaximum) >= 0 { - return fmt.Errorf("exclusiveMaximum: %s is greater than or equal to %f", n, *schema.ExclusiveMaximum) - } - } - } - - // strings: https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.3 - if instance.Kind() == reflect.String && (schema.MinLength != nil || schema.MaxLength != nil || schema.Pattern != "") { - str := instance.String() - n := utf8.RuneCountInString(str) - if schema.MinLength != nil { - if m := *schema.MinLength; n < m { - return fmt.Errorf("minLength: %q contains %d Unicode code points, fewer than %d", str, n, m) - } - } - if schema.MaxLength != nil { - if m := *schema.MaxLength; n > m { - return fmt.Errorf("maxLength: %q contains %d Unicode code points, more than %d", str, n, m) - } - } - - if schema.Pattern != "" && !schemaInfo.pattern.MatchString(str) { - return fmt.Errorf("pattern: %q does not match regular expression %q", str, schema.Pattern) - } - } - - var anns annotations // all the annotations for this call and child calls - - // $ref: https://json-schema.org/draft/2020-12/json-schema-core#section-8.2.3.1 - if schema.Ref != "" { - if err := st.validate(instance, schemaInfo.resolvedRef, &anns); err != nil { - return err - } - } - - // $dynamicRef: https://json-schema.org/draft/2020-12/json-schema-core#section-8.2.3.2 - if schema.DynamicRef != "" { - // The ref behaves lexically or dynamically, but not both. - assert((schemaInfo.resolvedDynamicRef == nil) != (schemaInfo.dynamicRefAnchor == ""), - "DynamicRef not resolved properly") - if schemaInfo.resolvedDynamicRef != nil { - // Same as $ref. - if err := st.validate(instance, schemaInfo.resolvedDynamicRef, &anns); err != nil { - return err - } - } else { - // Dynamic behavior. - // Look for the base of the outermost schema on the stack with this dynamic - // anchor. (Yes, outermost: the one farthest from here. This the opposite - // of how ordinary dynamic variables behave.) - // Why the base of the schema being validated and not the schema itself? - // Because the base is the scope for anchors. In fact it's possible to - // refer to a schema that is not on the stack, but a child of some base - // on the stack. - // For an example, search for "detached" in testdata/draft2020-12/dynamicRef.json. - var dynamicSchema *Schema - for _, s := range st.stack { - base := st.rs.resolvedInfos[s].base - info, ok := st.rs.resolvedInfos[base].anchors[schemaInfo.dynamicRefAnchor] - if ok && info.dynamic { - dynamicSchema = info.schema - break - } - } - if dynamicSchema == nil { - return fmt.Errorf("missing dynamic anchor %q", schemaInfo.dynamicRefAnchor) - } - if err := st.validate(instance, dynamicSchema, &anns); err != nil { - return err - } - } - } - - // logic - // https://json-schema.org/draft/2020-12/json-schema-core#section-10.2 - // These must happen before arrays and objects because if they evaluate an item or property, - // then the unevaluatedItems/Properties schemas don't apply to it. - // See https://json-schema.org/draft/2020-12/json-schema-core#section-11.2, paragraph 4. - // - // If any of these fail, then validation fails, even if there is an unevaluatedXXX - // keyword in the schema. The spec is unclear about this, but that is the intention. - - valid := func(s *Schema, anns *annotations) bool { return st.validate(instance, s, anns) == nil } - - if schema.AllOf != nil { - for _, ss := range schema.AllOf { - if err := st.validate(instance, ss, &anns); err != nil { - return err - } - } - } - if schema.AnyOf != nil { - // We must visit them all, to collect annotations. - ok := false - for _, ss := range schema.AnyOf { - if valid(ss, &anns) { - ok = true - } - } - if !ok { - return fmt.Errorf("anyOf: did not validate against any of %v", schema.AnyOf) - } - } - if schema.OneOf != nil { - // Exactly one. - var okSchema *Schema - for _, ss := range schema.OneOf { - if valid(ss, &anns) { - if okSchema != nil { - return fmt.Errorf("oneOf: validated against both %v and %v", okSchema, ss) - } - okSchema = ss - } - } - if okSchema == nil { - return fmt.Errorf("oneOf: did not validate against any of %v", schema.OneOf) - } - } - if schema.Not != nil { - // Ignore annotations from "not". - if valid(schema.Not, nil) { - return fmt.Errorf("not: validated against %v", schema.Not) - } - } - if schema.If != nil { - var ss *Schema - if valid(schema.If, &anns) { - ss = schema.Then - } else { - ss = schema.Else - } - if ss != nil { - if err := st.validate(instance, ss, &anns); err != nil { - return err - } - } - } - - // arrays - // TODO(jba): consider arrays of structs. - if instance.Kind() == reflect.Array || instance.Kind() == reflect.Slice { - // https://json-schema.org/draft/2020-12/json-schema-core#section-10.3.1 - // This validate call doesn't collect annotations for the items of the instance; they are separate - // instances in their own right. - // TODO(jba): if the test suite doesn't cover this case, add a test. For example, nested arrays. - for i, ischema := range schema.PrefixItems { - if i >= instance.Len() { - break // shorter is OK - } - if err := st.validate(instance.Index(i), ischema, nil); err != nil { - return err - } - } - anns.noteEndIndex(min(len(schema.PrefixItems), instance.Len())) - - if schema.Items != nil { - for i := len(schema.PrefixItems); i < instance.Len(); i++ { - if err := st.validate(instance.Index(i), schema.Items, nil); err != nil { - return err - } - } - // Note that all the items in this array have been validated. - anns.allItems = true - } - - nContains := 0 - if schema.Contains != nil { - for i := range instance.Len() { - if err := st.validate(instance.Index(i), schema.Contains, nil); err == nil { - nContains++ - anns.noteIndex(i) - } - } - if nContains == 0 && (schema.MinContains == nil || *schema.MinContains > 0) { - return fmt.Errorf("contains: %s does not have an item matching %s", instance, schema.Contains) - } - } - - // https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.4 - // TODO(jba): check that these next four keywords' values are integers. - if schema.MinContains != nil && schema.Contains != nil { - if m := *schema.MinContains; nContains < m { - return fmt.Errorf("minContains: contains validated %d items, less than %d", nContains, m) - } - } - if schema.MaxContains != nil && schema.Contains != nil { - if m := *schema.MaxContains; nContains > m { - return fmt.Errorf("maxContains: contains validated %d items, greater than %d", nContains, m) - } - } - if schema.MinItems != nil { - if m := *schema.MinItems; instance.Len() < m { - return fmt.Errorf("minItems: array length %d is less than %d", instance.Len(), m) - } - } - if schema.MaxItems != nil { - if m := *schema.MaxItems; instance.Len() > m { - return fmt.Errorf("maxItems: array length %d is greater than %d", instance.Len(), m) - } - } - if schema.UniqueItems { - if instance.Len() > 1 { - // Hash each item and compare the hashes. - // If two hashes differ, the items differ. - // If two hashes are the same, compare the collisions for equality. - // (The same logic as hash table lookup.) - // TODO(jba): Use container/hash.Map when it becomes available (https://go.dev/issue/69559), - hashes := map[uint64][]int{} // from hash to indices - seed := maphash.MakeSeed() - for i := range instance.Len() { - item := instance.Index(i) - var h maphash.Hash - h.SetSeed(seed) - hashValue(&h, item) - hv := h.Sum64() - if sames := hashes[hv]; len(sames) > 0 { - for _, j := range sames { - if equalValue(item, instance.Index(j)) { - return fmt.Errorf("uniqueItems: array items %d and %d are equal", i, j) - } - } - } - hashes[hv] = append(hashes[hv], i) - } - } - } - - // https://json-schema.org/draft/2020-12/json-schema-core#section-11.2 - if schema.UnevaluatedItems != nil && !anns.allItems { - // Apply this subschema to all items in the array that haven't been successfully validated. - // That includes validations by subschemas on the same instance, like allOf. - for i := anns.endIndex; i < instance.Len(); i++ { - if !anns.evaluatedIndexes[i] { - if err := st.validate(instance.Index(i), schema.UnevaluatedItems, nil); err != nil { - return err - } - } - } - anns.allItems = true - } - } - - // objects - // https://json-schema.org/draft/2020-12/json-schema-core#section-10.3.2 - if instance.Kind() == reflect.Map || instance.Kind() == reflect.Struct { - if instance.Kind() == reflect.Map { - if kt := instance.Type().Key(); kt.Kind() != reflect.String { - return fmt.Errorf("map key type %s is not a string", kt) - } - } - // Track the evaluated properties for just this schema, to support additionalProperties. - // If we used anns here, then we'd be including properties evaluated in subschemas - // from allOf, etc., which additionalProperties shouldn't observe. - evalProps := map[string]bool{} - for prop, subschema := range schema.Properties { - val := property(instance, prop) - if !val.IsValid() { - // It's OK if the instance doesn't have the property. - continue - } - // If the instance is a struct and an optional property has the zero - // value, then we could interpret it as present or missing. Be generous: - // assume it's missing, and thus always validates successfully. - if instance.Kind() == reflect.Struct && val.IsZero() && !schemaInfo.isRequired[prop] { - continue - } - if err := st.validate(val, subschema, nil); err != nil { - return err - } - evalProps[prop] = true - } - if len(schema.PatternProperties) > 0 { - for prop, val := range properties(instance) { - // Check every matching pattern. - for re, schema := range schemaInfo.patternProperties { - if re.MatchString(prop) { - if err := st.validate(val, schema, nil); err != nil { - return err - } - evalProps[prop] = true - } - } - } - } - if schema.AdditionalProperties != nil { - // Apply to all properties not handled above. - for prop, val := range properties(instance) { - if !evalProps[prop] { - if err := st.validate(val, schema.AdditionalProperties, nil); err != nil { - return err - } - evalProps[prop] = true - } - } - } - anns.noteProperties(evalProps) - if schema.PropertyNames != nil { - // Note: properties unnecessarily fetches each value. We could define a propertyNames function - // if performance ever matters. - for prop := range properties(instance) { - if err := st.validate(reflect.ValueOf(prop), schema.PropertyNames, nil); err != nil { - return err - } - } - } - - // https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01#section-6.5 - var min, max int - if schema.MinProperties != nil || schema.MaxProperties != nil { - min, max = numPropertiesBounds(instance, schemaInfo.isRequired) - } - if schema.MinProperties != nil { - if n, m := max, *schema.MinProperties; n < m { - return fmt.Errorf("minProperties: object has %d properties, less than %d", n, m) - } - } - if schema.MaxProperties != nil { - if n, m := min, *schema.MaxProperties; n > m { - return fmt.Errorf("maxProperties: object has %d properties, greater than %d", n, m) - } - } - - hasProperty := func(prop string) bool { - return property(instance, prop).IsValid() - } - - missingProperties := func(props []string) []string { - var missing []string - for _, p := range props { - if !hasProperty(p) { - missing = append(missing, p) - } - } - return missing - } - - if schema.Required != nil { - if m := missingProperties(schema.Required); len(m) > 0 { - return fmt.Errorf("required: missing properties: %q", m) - } - } - if schema.DependentRequired != nil { - // "Validation succeeds if, for each name that appears in both the instance - // and as a name within this keyword's value, every item in the corresponding - // array is also the name of a property in the instance." ยง6.5.4 - for dprop, reqs := range schema.DependentRequired { - if hasProperty(dprop) { - if m := missingProperties(reqs); len(m) > 0 { - return fmt.Errorf("dependentRequired[%q]: missing properties %q", dprop, m) - } - } - } - } - - // https://json-schema.org/draft/2020-12/json-schema-core#section-10.2.2.4 - if schema.DependentSchemas != nil { - // This does not collect annotations, although it seems like it should. - for dprop, ss := range schema.DependentSchemas { - if hasProperty(dprop) { - // TODO: include dependentSchemas[dprop] in the errors. - err := st.validate(instance, ss, &anns) - if err != nil { - return err - } - } - } - } - if schema.UnevaluatedProperties != nil && !anns.allProperties { - // This looks a lot like AdditionalProperties, but depends on in-place keywords like allOf - // in addition to sibling keywords. - for prop, val := range properties(instance) { - if !anns.evaluatedProperties[prop] { - if err := st.validate(val, schema.UnevaluatedProperties, nil); err != nil { - return err - } - } - } - // The spec says the annotation should be the set of evaluated properties, but we can optimize - // by setting a single boolean, since after this succeeds all properties will be validated. - // See https://json-schema.slack.com/archives/CT7FF623C/p1745592564381459. - anns.allProperties = true - } - } - - if callerAnns != nil { - // Our caller wants to know what we've validated. - callerAnns.merge(&anns) - } - return nil -} - -// resolveDynamicRef returns the schema referred to by the argument schema's -// $dynamicRef value. -// It returns an error if the dynamic reference has no referent. -// If there is no $dynamicRef, resolveDynamicRef returns nil, nil. -// See https://json-schema.org/draft/2020-12/json-schema-core#section-8.2.3.2. -func (st *state) resolveDynamicRef(schema *Schema) (*Schema, error) { - if schema.DynamicRef == "" { - return nil, nil - } - info := st.rs.resolvedInfos[schema] - // The ref behaves lexically or dynamically, but not both. - assert((info.resolvedDynamicRef == nil) != (info.dynamicRefAnchor == ""), - "DynamicRef not statically resolved properly") - if r := info.resolvedDynamicRef; r != nil { - // Same as $ref. - return r, nil - } - // Dynamic behavior. - // Look for the base of the outermost schema on the stack with this dynamic - // anchor. (Yes, outermost: the one farthest from here. This the opposite - // of how ordinary dynamic variables behave.) - // Why the base of the schema being validated and not the schema itself? - // Because the base is the scope for anchors. In fact it's possible to - // refer to a schema that is not on the stack, but a child of some base - // on the stack. - // For an example, search for "detached" in testdata/draft2020-12/dynamicRef.json. - for _, s := range st.stack { - base := st.rs.resolvedInfos[s].base - info, ok := st.rs.resolvedInfos[base].anchors[info.dynamicRefAnchor] - if ok && info.dynamic { - return info.schema, nil - } - } - return nil, fmt.Errorf("missing dynamic anchor %q", info.dynamicRefAnchor) -} - -// ApplyDefaults modifies an instance by applying the schema's defaults to it. If -// a schema or sub-schema has a default, then a corresponding zero instance value -// is set to the default. -// -// The JSON Schema specification does not describe how defaults should be interpreted. -// This method honors defaults only on properties, and only those that are not required. -// If the instance is a map and the property is missing, the property is added to -// the map with the default. -// If the instance is a struct, the field corresponding to the property exists, and -// its value is zero, the field is set to the default. -// ApplyDefaults can panic if a default cannot be assigned to a field. -// -// The argument must be a pointer to the instance. -// (In case we decide that top-level defaults are meaningful.) -// -// It is recommended to first call Resolve with a ValidateDefaults option of true, -// then call this method, and lastly call Validate. -// -// TODO(jba): consider what defaults on top-level or array instances might mean. -// TODO(jba): follow $ref and $dynamicRef -// TODO(jba): apply defaults on sub-schemas to corresponding sub-instances. -func (rs *Resolved) ApplyDefaults(instancep any) error { - st := &state{rs: rs} - return st.applyDefaults(reflect.ValueOf(instancep), rs.root) -} - -// Leave this as a potentially recursive helper function, because we'll surely want -// to apply defaults on sub-schemas someday. -func (st *state) applyDefaults(instancep reflect.Value, schema *Schema) (err error) { - defer wrapf(&err, "applyDefaults: schema %s, instance %v", st.rs.schemaString(schema), instancep) - - schemaInfo := st.rs.resolvedInfos[schema] - instance := instancep.Elem() - if instance.Kind() == reflect.Map || instance.Kind() == reflect.Struct { - if instance.Kind() == reflect.Map { - if kt := instance.Type().Key(); kt.Kind() != reflect.String { - return fmt.Errorf("map key type %s is not a string", kt) - } - } - for prop, subschema := range schema.Properties { - // Ignore defaults on required properties. (A required property shouldn't have a default.) - if schemaInfo.isRequired[prop] { - continue - } - val := property(instance, prop) - switch instance.Kind() { - case reflect.Map: - // If there is a default for this property, and the map key is missing, - // set the map value to the default. - if subschema.Default != nil && !val.IsValid() { - // Create an lvalue, since map values aren't addressable. - lvalue := reflect.New(instance.Type().Elem()) - if err := json.Unmarshal(subschema.Default, lvalue.Interface()); err != nil { - return err - } - instance.SetMapIndex(reflect.ValueOf(prop), lvalue.Elem()) - } - case reflect.Struct: - // If there is a default for this property, and the field exists but is zero, - // set the field to the default. - if subschema.Default != nil && val.IsValid() && val.IsZero() { - if err := json.Unmarshal(subschema.Default, val.Addr().Interface()); err != nil { - return err - } - } - default: - panic(fmt.Sprintf("applyDefaults: property %s: bad value %s of kind %s", - prop, instance, instance.Kind())) - } - } - } - return nil -} - -// property returns the value of the property of v with the given name, or the invalid -// reflect.Value if there is none. -// If v is a map, the property is the value of the map whose key is name. -// If v is a struct, the property is the value of the field with the given name according -// to the encoding/json package (see [jsonName]). -// If v is anything else, property panics. -func property(v reflect.Value, name string) reflect.Value { - switch v.Kind() { - case reflect.Map: - return v.MapIndex(reflect.ValueOf(name)) - case reflect.Struct: - props := structPropertiesOf(v.Type()) - // Ignore nonexistent properties. - if sf, ok := props[name]; ok { - return v.FieldByIndex(sf.Index) - } - return reflect.Value{} - default: - panic(fmt.Sprintf("property(%q): bad value %s of kind %s", name, v, v.Kind())) - } -} - -// properties returns an iterator over the names and values of all properties -// in v, which must be a map or a struct. -// If a struct, zero-valued properties that are marked omitempty or omitzero -// are excluded. -func properties(v reflect.Value) iter.Seq2[string, reflect.Value] { - return func(yield func(string, reflect.Value) bool) { - switch v.Kind() { - case reflect.Map: - for k, e := range v.Seq2() { - if !yield(k.String(), e) { - return - } - } - case reflect.Struct: - for name, sf := range structPropertiesOf(v.Type()) { - val := v.FieldByIndex(sf.Index) - if val.IsZero() { - info := fieldJSONInfo(sf) - if info.settings["omitempty"] || info.settings["omitzero"] { - continue - } - } - if !yield(name, val) { - return - } - } - default: - panic(fmt.Sprintf("bad value %s of kind %s", v, v.Kind())) - } - } -} - -// numPropertiesBounds returns bounds on the number of v's properties. -// v must be a map or a struct. -// If v is a map, both bounds are the map's size. -// If v is a struct, the max is the number of struct properties. -// But since we don't know whether a zero value indicates a missing optional property -// or not, be generous and use the number of non-zero properties as the min. -func numPropertiesBounds(v reflect.Value, isRequired map[string]bool) (int, int) { - switch v.Kind() { - case reflect.Map: - return v.Len(), v.Len() - case reflect.Struct: - sp := structPropertiesOf(v.Type()) - min := 0 - for prop, sf := range sp { - if !v.FieldByIndex(sf.Index).IsZero() || isRequired[prop] { - min++ - } - } - return min, len(sp) - default: - panic(fmt.Sprintf("properties: bad value: %s of kind %s", v, v.Kind())) - } -} - -// A propertyMap is a map from property name to struct field index. -type propertyMap = map[string]reflect.StructField - -var structProperties sync.Map // from reflect.Type to propertyMap - -// structPropertiesOf returns the JSON Schema properties for the struct type t. -// The caller must not mutate the result. -func structPropertiesOf(t reflect.Type) propertyMap { - // Mutex not necessary: at worst we'll recompute the same value. - if props, ok := structProperties.Load(t); ok { - return props.(propertyMap) - } - props := map[string]reflect.StructField{} - for _, sf := range reflect.VisibleFields(t) { - info := fieldJSONInfo(sf) - if !info.omit { - props[info.name] = sf - } - } - structProperties.Store(t, props) - return props -} diff --git a/jsonschema/validate_test.go b/jsonschema/validate_test.go deleted file mode 100644 index 7fb52d18..00000000 --- a/jsonschema/validate_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2025 The Go MCP SDK Authors. All rights reserved. -// Use of this source code is governed by an MIT-style -// license that can be found in the LICENSE file. - -package jsonschema - -import ( - "encoding/json" - "fmt" - "net/url" - "os" - "path/filepath" - "reflect" - "strings" - "testing" -) - -// The test for validation uses the official test suite, expressed as a set of JSON files. -// Each file is an array of group objects. - -// A testGroup consists of a schema and some tests on it. -type testGroup struct { - Description string - Schema *Schema - Tests []test -} - -// A test consists of a JSON instance to be validated and the expected result. -type test struct { - Description string - Data any - Valid bool -} - -func TestValidate(t *testing.T) { - files, err := filepath.Glob(filepath.FromSlash("testdata/draft2020-12/*.json")) - if err != nil { - t.Fatal(err) - } - if len(files) == 0 { - t.Fatal("no files") - } - for _, file := range files { - base := filepath.Base(file) - t.Run(base, func(t *testing.T) { - data, err := os.ReadFile(file) - if err != nil { - t.Fatal(err) - } - var groups []testGroup - if err := json.Unmarshal(data, &groups); err != nil { - t.Fatal(err) - } - for _, g := range groups { - t.Run(g.Description, func(t *testing.T) { - rs, err := g.Schema.Resolve(&ResolveOptions{Loader: loadRemote}) - if err != nil { - t.Fatal(err) - } - for _, test := range g.Tests { - t.Run(test.Description, func(t *testing.T) { - err = rs.Validate(test.Data) - if err != nil && test.Valid { - t.Errorf("wanted success, but failed with: %v", err) - } - if err == nil && !test.Valid { - t.Error("succeeded but wanted failure") - } - if t.Failed() { - t.Errorf("schema: %s", g.Schema.json()) - t.Fatalf("instance: %v (%[1]T)", test.Data) - } - }) - } - }) - } - }) - } -} - -func TestValidateErrors(t *testing.T) { - schema := &Schema{ - PrefixItems: []*Schema{{Contains: &Schema{Type: "integer"}}}, - } - rs, err := schema.Resolve(nil) - if err != nil { - t.Fatal(err) - } - err = rs.Validate([]any{[]any{"1"}}) - want := "prefixItems/0" - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("error:\n%s\ndoes not contain %q", err, want) - } -} - -func TestValidateDefaults(t *testing.T) { - s := &Schema{ - Properties: map[string]*Schema{ - "a": {Type: "integer", Default: mustMarshal(1)}, - "b": {Type: "string", Default: mustMarshal("s")}, - }, - Default: mustMarshal(map[string]any{"a": 1, "b": "two"}), - } - if _, err := s.Resolve(&ResolveOptions{ValidateDefaults: true}); err != nil { - t.Fatal(err) - } - - s = &Schema{ - Properties: map[string]*Schema{ - "a": {Type: "integer", Default: mustMarshal(3)}, - "b": {Type: "string", Default: mustMarshal("s")}, - }, - Default: mustMarshal(map[string]any{"a": 1, "b": 2}), - } - _, err := s.Resolve(&ResolveOptions{ValidateDefaults: true}) - want := `has type "integer", want "string"` - if err == nil || !strings.Contains(err.Error(), want) { - t.Errorf("Resolve returned error %q, want %q", err, want) - } -} - -func TestApplyDefaults(t *testing.T) { - schema := &Schema{ - Properties: map[string]*Schema{ - "A": {Default: mustMarshal(1)}, - "B": {Default: mustMarshal(2)}, - "C": {Default: mustMarshal(3)}, - }, - Required: []string{"C"}, - } - rs, err := schema.Resolve(&ResolveOptions{ValidateDefaults: true}) - if err != nil { - t.Fatal(err) - } - - type S struct{ A, B, C int } - for _, tt := range []struct { - instancep any // pointer to instance value - want any // desired value (not a pointer) - }{ - { - &map[string]any{"B": 0}, - map[string]any{ - "A": float64(1), // filled from default - "B": 0, // untouched: it was already there - // "C" not added: it is required (Validate will catch that) - }, - }, - { - &S{B: 1}, - S{ - A: 1, // filled from default - B: 1, // untouched: non-zero - C: 0, // untouched: required - }, - }, - } { - if err := rs.ApplyDefaults(tt.instancep); err != nil { - t.Fatal(err) - } - got := reflect.ValueOf(tt.instancep).Elem().Interface() // dereference the pointer - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("\ngot %#v\nwant %#v", got, tt.want) - } - } -} - -func TestStructInstance(t *testing.T) { - instance := struct { - I int - B bool `json:"b"` - P *int // either missing or nil - u int // unexported: not a property - }{1, true, nil, 0} - - for _, tt := range []struct { - s Schema - want bool - }{ - { - Schema{MinProperties: Ptr(4)}, - false, - }, - { - Schema{MinProperties: Ptr(3)}, - true, // P interpreted as present - }, - { - Schema{MaxProperties: Ptr(1)}, - false, - }, - { - Schema{MaxProperties: Ptr(2)}, - true, // P interpreted as absent - }, - { - Schema{Required: []string{"i"}}, // the name is "I" - false, - }, - { - Schema{Required: []string{"B"}}, // the name is "b" - false, - }, - { - Schema{PropertyNames: &Schema{MinLength: Ptr(2)}}, - false, - }, - { - Schema{Properties: map[string]*Schema{"b": {Type: "boolean"}}}, - true, - }, - { - Schema{Properties: map[string]*Schema{"b": {Type: "number"}}}, - false, - }, - { - Schema{Required: []string{"I"}}, - true, - }, - { - Schema{Required: []string{"I", "P"}}, - true, // P interpreted as present - }, - { - Schema{Required: []string{"I", "P"}, Properties: map[string]*Schema{"P": {Type: "number"}}}, - false, // P interpreted as present, but not a number - }, - { - Schema{Required: []string{"I"}, Properties: map[string]*Schema{"P": {Type: "number"}}}, - true, // P not required, so interpreted as absent - }, - { - Schema{Required: []string{"I"}, AdditionalProperties: falseSchema()}, - false, - }, - { - Schema{DependentRequired: map[string][]string{"b": {"u"}}}, - false, - }, - { - Schema{DependentSchemas: map[string]*Schema{"b": falseSchema()}}, - false, - }, - { - Schema{UnevaluatedProperties: falseSchema()}, - false, - }, - } { - res, err := tt.s.Resolve(nil) - if err != nil { - t.Fatal(err) - } - err = res.Validate(instance) - if err == nil && !tt.want { - t.Errorf("succeeded unexpectedly\nschema = %s", tt.s.json()) - } else if err != nil && tt.want { - t.Errorf("Validate: %v\nschema = %s", err, tt.s.json()) - } - } -} - -func mustMarshal(x any) json.RawMessage { - data, err := json.Marshal(x) - if err != nil { - panic(err) - } - return json.RawMessage(data) -} - -// loadRemote loads a remote reference used in the test suite. -func loadRemote(uri *url.URL) (*Schema, error) { - // Anything with localhost:1234 refers to the remotes directory in the test suite repo. - if uri.Host == "localhost:1234" { - return loadSchemaFromFile(filepath.FromSlash(filepath.Join("testdata/remotes", uri.Path))) - } - // One test needs the meta-schema files. - const metaPrefix = "https://json-schema.org/draft/2020-12/" - if after, ok := strings.CutPrefix(uri.String(), metaPrefix); ok { - return loadSchemaFromFile(filepath.FromSlash("meta-schemas/draft2020-12/" + after + ".json")) - } - return nil, fmt.Errorf("don't know how to load %s", uri) -} - -func loadSchemaFromFile(filename string) (*Schema, error) { - data, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - var s Schema - if err := json.Unmarshal(data, &s); err != nil { - return nil, fmt.Errorf("unmarshaling JSON at %s: %w", filename, err) - } - return &s, nil -} diff --git a/mcp/client_list_test.go b/mcp/client_list_test.go index 497a9cd0..5b13a4c8 100644 --- a/mcp/client_list_test.go +++ b/mcp/client_list_test.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/client_test.go b/mcp/client_test.go index 73fe09e6..7920c55c 100644 --- a/mcp/client_test.go +++ b/mcp/client_test.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) type Item struct { diff --git a/mcp/example_middleware_test.go b/mcp/example_middleware_test.go index 1328473a..597b9dcd 100644 --- a/mcp/example_middleware_test.go +++ b/mcp/example_middleware_test.go @@ -11,7 +11,7 @@ import ( "os" "time" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/mcp" ) diff --git a/mcp/features_test.go b/mcp/features_test.go index e0165ecb..1c22ecd3 100644 --- a/mcp/features_test.go +++ b/mcp/features_test.go @@ -11,7 +11,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) type SayHiParams struct { diff --git a/mcp/mcp_test.go b/mcp/mcp_test.go index da53465c..48e95de2 100644 --- a/mcp/mcp_test.go +++ b/mcp/mcp_test.go @@ -21,8 +21,8 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2" - "github.com/modelcontextprotocol/go-sdk/jsonschema" ) type hiParams struct { diff --git a/mcp/protocol.go b/mcp/protocol.go index 3ca6cb5e..d80a0787 100644 --- a/mcp/protocol.go +++ b/mcp/protocol.go @@ -14,7 +14,7 @@ import ( "encoding/json" "fmt" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) // Optional annotations for the client. The client can use annotations to inform diff --git a/mcp/server_test.go b/mcp/server_test.go index 5a161b72..6415decc 100644 --- a/mcp/server_test.go +++ b/mcp/server_test.go @@ -11,7 +11,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) type testItem struct { diff --git a/mcp/streamable_test.go b/mcp/streamable_test.go index af06bd39..849f6026 100644 --- a/mcp/streamable_test.go +++ b/mcp/streamable_test.go @@ -24,9 +24,9 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/google/jsonschema-go/jsonschema" "github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2" "github.com/modelcontextprotocol/go-sdk/jsonrpc" - "github.com/modelcontextprotocol/go-sdk/jsonschema" ) func TestStreamableTransports(t *testing.T) { diff --git a/mcp/tool.go b/mcp/tool.go index ed80b660..234cd659 100644 --- a/mcp/tool.go +++ b/mcp/tool.go @@ -11,7 +11,7 @@ import ( "fmt" "reflect" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) // A ToolHandler handles a call to tools/call. diff --git a/mcp/tool_test.go b/mcp/tool_test.go index 4d0a329b..52cac9fc 100644 --- a/mcp/tool_test.go +++ b/mcp/tool_test.go @@ -12,7 +12,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/modelcontextprotocol/go-sdk/jsonschema" + "github.com/google/jsonschema-go/jsonschema" ) // testToolHandler is used for type inference in TestNewServerTool.