From 050149817414cd405a7301adce30193d27a290dc Mon Sep 17 00:00:00 2001 From: Johan Fylling Date: Fri, 24 Feb 2023 17:14:19 +0100 Subject: [PATCH] Addressing review comments * Merging `jsonschema.is_valid` into `jsonschema.verify`, and `json.is_match_schema` into `json.match_schema` * Adding yaml test cases in testdata Signed-off-by: Johan Fylling --- ast/builtins.go | 69 +++--- builtin_metadata.json | 63 +----- capabilities.json | 161 +++++-------- .../jsonschema/test-json-match_schema.yaml | 97 ++++++++ .../jsonschema/test-jsonschema-verify.yaml | 41 ++++ topdown/jsonschema.go | 74 +----- topdown/jsonschema_test.go | 213 ++---------------- 7 files changed, 251 insertions(+), 467 deletions(-) create mode 100644 test/cases/testdata/jsonschema/test-json-match_schema.yaml create mode 100644 test/cases/testdata/jsonschema/test-jsonschema-verify.yaml diff --git a/ast/builtins.go b/ast/builtins.go index 78923dff12..c25fb3cb93 100644 --- a/ast/builtins.go +++ b/ast/builtins.go @@ -245,9 +245,7 @@ var DefaultBuiltins = [...]*Builtin{ GraphQLSchemaIsValid, // JSON Schema - JSONSchemaIsValid, JSONSchemaVerify, - JSONIsMatchSchema, JSONMatchSchema, // Cloud Provider Helpers @@ -2663,19 +2661,6 @@ var GraphQLSchemaIsValid = &Builtin{ * JSON Schema */ -// JSONSchemaIsValid returns true if the input is valid JSON schema, -// and returns false for all other inputs. -var JSONSchemaIsValid = &Builtin{ - Name: "jsonschema.is_valid", - Description: "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", - Decl: types.NewFunction( - types.Args( - types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), - ), - types.Named("output", types.B).Description("`true` if the schema is a valid JSON schema. `false` otherwise."), - ), -} - // JSONSchemaVerify returns empty string if the input is valid JSON schema // and returns error string for all other inputs. var JSONSchemaVerify = &Builtin{ @@ -2683,23 +2668,16 @@ var JSONSchemaVerify = &Builtin{ Description: "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", Decl: types.NewFunction( types.Args( - types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("the schema to verify"), ), - types.Named("output", types.S).Description("empty string if the schema is a valid JSON schema. Error string otherwise."), - ), -} - -// JSONIsMatchSchema returns true if the document matches the JSON schema, otherwise false. -var JSONIsMatchSchema = &Builtin{ - Name: "json.is_match_schema", - Description: "Checks that the document matches the JSON schema.", - Decl: types.NewFunction( - types.Args( - types.Named("document", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), - types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), - ), - types.Named("output", types.B).Description("`true` if the document matches the JSON schema. Otherwise `false`."), + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewAny(types.S, types.Null{}), + }, nil)). + Description("`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error."), ), + Categories: objectCat, } // JSONMatchSchema returns empty array if the document matches the JSON schema, @@ -2709,21 +2687,28 @@ var JSONMatchSchema = &Builtin{ Description: "Checks that the document matches the JSON schema.", Decl: types.NewFunction( types.Args( - types.Named("document", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), - types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))), + types.Named("document", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("document to verify by schema"), + types.Named("schema", types.NewAny(types.S, types.NewObject(nil, types.NewDynamicProperty(types.A, types.A)))). + Description("schema to verify document by"), ), - types.Named("output", types.NewArray( - nil, types.NewObject( - []*types.StaticProperty{ - {Key: "error", Value: types.S}, - {Key: "type", Value: types.S}, - {Key: "field", Value: types.S}, - {Key: "desc", Value: types.S}, - }, - nil, + types.Named("output", types.NewArray([]types.Type{ + types.B, + types.NewArray( + nil, types.NewObject( + []*types.StaticProperty{ + {Key: "error", Value: types.S}, + {Key: "type", Value: types.S}, + {Key: "field", Value: types.S}, + {Key: "desc", Value: types.S}, + }, + nil, + ), ), - )).Description("empty array if the document matches the JSON schema. Array of error objects otherwise."), + }, nil)). + Description("`output` is of the form `[match, errors]`. If the document is valid given the schema, then `match` is `true`, and `errors` is an empty array. Otherwise, `match` is `true` and `errors` is an array of objects describing the error(s)."), ), + Categories: objectCat, } /** diff --git a/builtin_metadata.json b/builtin_metadata.json index a7a1ff34ed..985fab074b 100644 --- a/builtin_metadata.json +++ b/builtin_metadata.json @@ -90,14 +90,6 @@ "internal.member_3", "internal.print" ], - "json": [ - "json.is_match_schema", - "json.match_schema" - ], - "jsonschema": [ - "jsonschema.is_valid", - "jsonschema.verify" - ], "net": [ "net.cidr_contains", "net.cidr_contains_matches", @@ -123,8 +115,10 @@ ], "object": [ "json.filter", + "json.match_schema", "json.patch", "json.remove", + "jsonschema.verify", "object.filter", "object.get", "object.keys", @@ -8284,29 +8278,6 @@ }, "wasm": true }, - "json.is_match_schema": { - "args": [ - { - "name": "document", - "type": "any\u003cstring, object[any: any]\u003e" - }, - { - "name": "schema", - "type": "any\u003cstring, object[any: any]\u003e" - } - ], - "available": [ - "edge" - ], - "description": "Checks that the document matches the JSON schema.", - "introduced": "edge", - "result": { - "description": "`true` if the document matches the JSON schema. Otherwise `false`.", - "name": "output", - "type": "boolean" - }, - "wasm": false - }, "json.is_valid": { "args": [ { @@ -8486,10 +8457,12 @@ "json.match_schema": { "args": [ { + "description": "document to verify by schema", "name": "document", "type": "any\u003cstring, object[any: any]\u003e" }, { + "description": "schema to verify document by", "name": "schema", "type": "any\u003cstring, object[any: any]\u003e" } @@ -8500,9 +8473,9 @@ "description": "Checks that the document matches the JSON schema.", "introduced": "edge", "result": { - "description": "empty array if the document matches the JSON schema. Array of error objects otherwise.", + "description": "`output` is of the form `[match, errors]`. If the document is valid given the schema, then `match` is `true`, and `errors` is an empty array. Otherwise, `match` is `true` and `errors` is an array of objects describing the error(s).", "name": "output", - "type": "array[object\u003cdesc: string, error: string, field: string, type: string\u003e]" + "type": "array\u003cboolean, array[object\u003cdesc: string, error: string, field: string, type: string\u003e]\u003e" }, "wasm": false }, @@ -8780,28 +8753,10 @@ }, "wasm": true }, - "jsonschema.is_valid": { - "args": [ - { - "name": "schema", - "type": "any\u003cstring, object[any: any]\u003e" - } - ], - "available": [ - "edge" - ], - "description": "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", - "introduced": "edge", - "result": { - "description": "`true` if the schema is a valid JSON schema. `false` otherwise.", - "name": "output", - "type": "boolean" - }, - "wasm": false - }, "jsonschema.verify": { "args": [ { + "description": "the schema to verify", "name": "schema", "type": "any\u003cstring, object[any: any]\u003e" } @@ -8812,9 +8767,9 @@ "description": "Checks that the input is a valid JSON schema object. The schema can be either a JSON string or an JSON object.", "introduced": "edge", "result": { - "description": "empty string if the schema is a valid JSON schema. Error string otherwise.", + "description": "`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error.", "name": "output", - "type": "string" + "type": "array\u003cboolean, any\u003cnull, string\u003e\u003e" }, "wasm": false }, diff --git a/capabilities.json b/capabilities.json index b9df6c4d2f..dfa7f5ecbe 100644 --- a/capabilities.json +++ b/capabilities.json @@ -2052,55 +2052,6 @@ "type": "function" } }, - { - "name": "json.is_match_schema", - "decl": { - "args": [ - { - "of": [ - { - "type": "string" - }, - { - "dynamic": { - "key": { - "type": "any" - }, - "value": { - "type": "any" - } - }, - "type": "object" - } - ], - "type": "any" - }, - { - "of": [ - { - "type": "string" - }, - { - "dynamic": { - "key": { - "type": "any" - }, - "value": { - "type": "any" - } - }, - "type": "object" - } - ], - "type": "any" - } - ], - "result": { - "type": "boolean" - }, - "type": "function" - } - }, { "name": "json.is_valid", "decl": { @@ -2173,35 +2124,43 @@ } ], "result": { - "dynamic": { - "static": [ - { - "key": "desc", - "value": { - "type": "string" - } - }, - { - "key": "error", - "value": { - "type": "string" - } - }, - { - "key": "field", - "value": { - "type": "string" - } + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" }, - { - "key": "type", - "value": { - "type": "string" - } - } - ], - "type": "object" - }, + "type": "array" + } + ], "type": "array" }, "type": "function" @@ -2324,36 +2283,6 @@ "type": "function" } }, - { - "name": "jsonschema.is_valid", - "decl": { - "args": [ - { - "of": [ - { - "type": "string" - }, - { - "dynamic": { - "key": { - "type": "any" - }, - "value": { - "type": "any" - } - }, - "type": "object" - } - ], - "type": "any" - } - ], - "result": { - "type": "boolean" - }, - "type": "function" - } - }, { "name": "jsonschema.verify", "decl": { @@ -2379,7 +2308,23 @@ } ], "result": { - "type": "string" + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" }, "type": "function" } diff --git a/test/cases/testdata/jsonschema/test-json-match_schema.yaml b/test/cases/testdata/jsonschema/test-json-match_schema.yaml new file mode 100644 index 0000000000..f5f85d9ce5 --- /dev/null +++ b/test/cases/testdata/jsonschema/test-json-match_schema.yaml @@ -0,0 +1,97 @@ +cases: + - note: json_match_schema/success + modules: + - | + package test + + document := {"id": 5} + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [ true, [ ] ] + - note: json_match_schema/success string document + modules: + - | + package test + + document := `{"id": 5}` + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [ true, [ ] ] + - note: json_match_schema/success string schema + modules: + - | + package test + + document := {"id": 5} + schema := `{ + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + }` + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [ true, [ ] ] + - note: json_match_schema/invalid document + modules: + - | + package test + + document := {"id": "foo"} + schema := { + "properties": { + "id": { + "type": "integer" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + want_result: + - x: [ false, [ { + "desc": "Invalid type. Expected: integer, given: string", + "error": "id: Invalid type. Expected: integer, given: string", + "field": "id", + "type": "invalid_type" + } ] ] + - note: json_match_schema/invalid schema + modules: + - | + package test + + document := {"id": "foo"} + schema := { + "properties": { + "id": { + "type": "unknown" + } + }, + "required": ["id"] + } + p := json.match_schema(document, schema) + query: data.test.p = x + strict_error: true + want_error: 'json.match_schema: has a primitive type that is NOT VALID -- given: /unknown/ Expected valid values are:[array boolean integer number null object string]' + want_error_code: eval_builtin_error \ No newline at end of file diff --git a/test/cases/testdata/jsonschema/test-jsonschema-verify.yaml b/test/cases/testdata/jsonschema/test-jsonschema-verify.yaml new file mode 100644 index 0000000000..da2880cdd1 --- /dev/null +++ b/test/cases/testdata/jsonschema/test-jsonschema-verify.yaml @@ -0,0 +1,41 @@ +cases: + - note: jsonschema_verify/valid schema string + modules: + - | + package test + + schema := `{"type": "boolean"}` + p := jsonschema.verify(schema) + query: data.test.p = x + want_result: + - x: [ true, null ] + - note: jsonschema_verify/valid schema object + modules: + - | + package test + + schema := {"type": "boolean"} + p := jsonschema.verify(schema) + query: data.test.p = x + want_result: + - x: [ true, null ] + - note: jsonschema_verify/invalid schema string + modules: + - | + package test + + schema := `{"type": "unknown_type"}` + p := jsonschema.verify(schema) + query: data.test.p = x + want_result: + - x: [ false, "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]" ] + - note: jsonschema_verify/invalid schema object + modules: + - | + package test + + schema := {"type": "unknown_type"} + p := jsonschema.verify(schema) + query: data.test.p = x + want_result: + - x: [ false, "jsonschema: has a primitive type that is NOT VALID -- given: /unknown_type/ Expected valid values are:[array boolean integer number null object string]" ] \ No newline at end of file diff --git a/topdown/jsonschema.go b/topdown/jsonschema.go index 18c2691024..251792d3a1 100644 --- a/topdown/jsonschema.go +++ b/topdown/jsonschema.go @@ -48,42 +48,29 @@ func astValueToJSONSchemaLoader(value ast.Value) (gojsonschema.JSONLoader, error return loader, nil } -// builtinJSONSchemaIsValid accepts 1 argument which can be string or object and checks if it is valid JSON schema. -func builtinJSONSchemaIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - // Take first argument and make JSON Loader from it. - loader, err := astValueToJSONSchemaLoader(operands[0].Value) - if err != nil { - return iter(ast.BooleanTerm(false)) - } - - // Check that schema is correct and parses without errors. - if _, err = gojsonschema.NewSchema(loader); err != nil { - return iter(ast.BooleanTerm(false)) - } - - return iter(ast.BooleanTerm(true)) +func newResultTerm(valid bool, data *ast.Term) *ast.Term { + return ast.ArrayTerm(ast.BooleanTerm(valid), data) } // builtinJSONSchemaValidate accepts 1 argument which can be string or object and checks if it is valid JSON schema. -// Returns string in case of error or empty string otherwise. +// Returns array [false, ] with error string at index 1, or [true, ""] with empty string at index 1 otherwise. func builtinJSONSchemaValidate(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Take first argument and make JSON Loader from it. loader, err := astValueToJSONSchemaLoader(operands[0].Value) if err != nil { - return iter(ast.StringTerm("jsonschema: " + err.Error())) + return iter(newResultTerm(false, ast.StringTerm("jsonschema: "+err.Error()))) } // Check that schema is correct and parses without errors. if _, err = gojsonschema.NewSchema(loader); err != nil { - return iter(ast.StringTerm("jsonschema: " + err.Error())) + return iter(newResultTerm(false, ast.StringTerm("jsonschema: "+err.Error()))) } - return iter(ast.StringTerm("")) + return iter(newResultTerm(true, ast.NullTerm())) } // builtinJSONMatchSchema accepts 2 arguments both can be string or object and verifies if the document matches the JSON schema. -// Returns an array of errors or empty array. -// Returns an empty array if no errors are found. +// Returns an array where first element is a boolean indicating a successful match, and the second is an array of errors that is empty on success and populated on failure. // In case of internal error returns empty array. func builtinJSONMatchSchema(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Take first argument and make JSON Loader from it. @@ -100,14 +87,8 @@ func builtinJSONMatchSchema(_ BuiltinContext, operands []*ast.Term, iter func(*a return err } - // Make new schema instance to provide validations. - schema, err := gojsonschema.NewSchema(schemaLoader) - if err != nil { - return err - } - - // Use the schema instance to validate the document. - result, err := schema.Validate(documentLoader) + // Use schema to validate document. + result, err := gojsonschema.Validate(schemaLoader, documentLoader) if err != nil { return err } @@ -124,45 +105,10 @@ func builtinJSONMatchSchema(_ BuiltinContext, operands []*ast.Term, iter func(*a arr = arr.Append(ast.NewTerm(o)) } - return iter(ast.NewTerm(arr)) -} - -// builtinJSONIsMatchSchema accepts 2 arguments both can be string or object and verifies if the document matches the JSON schema. -// Returns true if the document matches the schema. -func builtinJSONIsMatchSchema(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - // Take first argument and make JSON Loader from it. - // This is a JSON document made from Rego JSON string or object. - documentLoader, err := astValueToJSONSchemaLoader(operands[0].Value) - if err != nil { - return err - } - - // Take second argument and make JSON Loader from it. - // This is a JSON schema made from Rego JSON string or object. - schemaLoader, err := astValueToJSONSchemaLoader(operands[1].Value) - if err != nil { - return err - } - - // Make new schema instance to provide validations. - schema, err := gojsonschema.NewSchema(schemaLoader) - if err != nil { - return err - } - - // Use the schema instance to validate the document. - result, err := schema.Validate(documentLoader) - if err != nil { - return err - } - - // Return true/false only without errors explanation. - return iter(ast.BooleanTerm(result.Valid())) + return iter(newResultTerm(result.Valid(), ast.NewTerm(arr))) } func init() { - RegisterBuiltinFunc(ast.JSONSchemaIsValid.Name, builtinJSONSchemaIsValid) RegisterBuiltinFunc(ast.JSONSchemaVerify.Name, builtinJSONSchemaValidate) - RegisterBuiltinFunc(ast.JSONIsMatchSchema.Name, builtinJSONIsMatchSchema) RegisterBuiltinFunc(ast.JSONMatchSchema.Name, builtinJSONMatchSchema) } diff --git a/topdown/jsonschema_test.go b/topdown/jsonschema_test.go index 8534eed22b..0c6c23a090 100644 --- a/topdown/jsonschema_test.go +++ b/topdown/jsonschema_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 The OPA Authors. All rights reserved. +// Copyright 2022 The OPA Authors. All rights reserved. // Use of this source code is governed by an Apache2 // license that can be found in the LICENSE file. @@ -82,85 +82,6 @@ func TestAstValueToJSONSchemaLoader(t *testing.T) { } } -func TestBuiltinJSONSchemaIsValid(t *testing.T) { - cases := []struct { - note string - schema ast.Value - result ast.Value - err bool - }{ - { - note: "string empty schema", - schema: ast.String(`{}`), - result: ast.Boolean(true), - err: false, - }, - { - note: "string broken JSON", - schema: ast.String(`{ "a": "`), - result: ast.Boolean(false), - err: false, - }, - { - note: "string simple schema", - schema: ast.String(` - { - "properties": { - "id": { - "type": "integer" - } - }, - "required": ["id"] - } - `), - result: ast.Boolean(true), - err: false, - }, - { - note: "string broken schema", - schema: ast.String(` - { - "properties": { - "id": { - "type": "UNKNOWN" - } - }, - "required": ["id"] - } - `), - result: ast.Boolean(false), - err: false, - }, - } - - for _, tc := range cases { - t.Run(tc.note, func(t *testing.T) { - result := ast.NullTerm().Value - err := builtinJSONSchemaIsValid( - BuiltinContext{}, - []*ast.Term{ast.NewTerm(tc.schema)}, - func(term *ast.Term) error { - result = term.Value - return nil - }, - ) - - if tc.err && err == nil { - t.Errorf("Unexpected schema validation, expected error, got nil") - return - } - if !tc.err && err != nil { - t.Errorf("Unexpected schema validation, expected nil, got error: %s", err) - return - } - if tc.result.Compare(result) != 0 { - t.Errorf("Unexpected schema validation, expected result %s, got result %s", tc.result.String(), result.String()) - return - } - }) - } -} - func TestBuiltinJSONSchemaValidate(t *testing.T) { cases := []struct { note string @@ -171,13 +92,13 @@ func TestBuiltinJSONSchemaValidate(t *testing.T) { { note: "string empty schema", schema: ast.String(`{}`), - result: ast.String(""), + result: ast.NewArray(ast.BooleanTerm(true), ast.NullTerm()), err: false, }, { note: "string broken JSON", schema: ast.String(`{ "a": "`), - result: ast.String("jsonschema: invalid JSON string"), + result: ast.NewArray(ast.BooleanTerm(false), ast.StringTerm("jsonschema: invalid JSON string")), err: false, }, { @@ -192,7 +113,7 @@ func TestBuiltinJSONSchemaValidate(t *testing.T) { "required": ["id"] } `), - result: ast.String(""), + result: ast.NewArray(ast.BooleanTerm(true), ast.NullTerm()), err: false, }, { @@ -207,7 +128,7 @@ func TestBuiltinJSONSchemaValidate(t *testing.T) { "required": ["id"] } `), - result: ast.String("jsonschema: has a primitive type that is NOT VALID -- given: /UNKNOWN/ Expected valid values are:[array boolean integer number null object string]"), + result: ast.NewArray(ast.BooleanTerm(false), ast.StringTerm("jsonschema: has a primitive type that is NOT VALID -- given: /UNKNOWN/ Expected valid values are:[array boolean integer number null object string]")), err: false, }, } @@ -252,7 +173,7 @@ func TestBuiltinJSONMatchSchema(t *testing.T) { note: "string empty document, empty schema", document: ast.String(`{}`), schema: ast.String(`{}`), - result: ast.NewArray(), + result: ast.NewArray(ast.BooleanTerm(true), ast.ArrayTerm()), err: false, }, { @@ -282,7 +203,7 @@ func TestBuiltinJSONMatchSchema(t *testing.T) { "required": ["id"] } `), - result: ast.NewArray(), + result: ast.NewArray(ast.BooleanTerm(true), ast.ArrayTerm()), err: false, }, { @@ -314,12 +235,13 @@ func TestBuiltinJSONMatchSchema(t *testing.T) { "required": ["id"] } `), - result: ast.NewArray(ast.NewTerm(ast.NewObject( - [...]*ast.Term{ast.StringTerm("error"), ast.StringTerm("id: Invalid type. Expected: integer, given: string")}, - [...]*ast.Term{ast.StringTerm("type"), ast.StringTerm("invalid_type")}, - [...]*ast.Term{ast.StringTerm("field"), ast.StringTerm("id")}, - [...]*ast.Term{ast.StringTerm("desc"), ast.StringTerm("Invalid type. Expected: integer, given: string")}, - ))), + result: ast.NewArray(ast.BooleanTerm(false), + ast.ArrayTerm(ast.NewTerm(ast.NewObject( + [...]*ast.Term{ast.StringTerm("error"), ast.StringTerm("id: Invalid type. Expected: integer, given: string")}, + [...]*ast.Term{ast.StringTerm("type"), ast.StringTerm("invalid_type")}, + [...]*ast.Term{ast.StringTerm("field"), ast.StringTerm("id")}, + [...]*ast.Term{ast.StringTerm("desc"), ast.StringTerm("Invalid type. Expected: integer, given: string")}, + )))), err: false, }, } @@ -351,110 +273,3 @@ func TestBuiltinJSONMatchSchema(t *testing.T) { }) } } - -func TestBuiltinJSONIsMatchSchema(t *testing.T) { - cases := []struct { - note string - document ast.Value - schema ast.Value - result ast.Value - err bool - }{ - { - note: "string empty document, empty schema", - document: ast.String(`{}`), - schema: ast.String(`{}`), - result: ast.Boolean(true), - err: false, - }, - { - note: "string empty document, broken schema", - document: ast.String(`{}`), - schema: ast.String(`{ "a": "`), - result: ast.NullTerm().Value, - err: true, - }, - { - note: "string broken document, empty schema", - document: ast.String(`{ "a": "`), - schema: ast.String(`{}`), - result: ast.NullTerm().Value, - err: true, - }, - { - note: "string correct document, simple schema", - document: ast.String(`{ "id": 5 }`), - schema: ast.String(` - { - "properties": { - "id": { - "type": "integer" - } - }, - "required": ["id"] - } - `), - result: ast.Boolean(true), - err: false, - }, - { - note: "string correct document, invalid schema", - document: ast.String(`{ "id": 5 }`), - schema: ast.String(` - { - "properties": { - "id": { - "type": "UNKNOWN" - } - }, - "required": ["id"] - } - `), - result: ast.NullTerm().Value, - err: true, - }, - { - note: "string invalid document, correct schema", - document: ast.String(`{ "id": "test" }`), - schema: ast.String(` - { - "properties": { - "id": { - "type": "integer" - } - }, - "required": ["id"] - } - `), - result: ast.Boolean(false), - err: false, - }, - } - - for _, tc := range cases { - t.Run(tc.note, func(t *testing.T) { - result := ast.NullTerm().Value - err := builtinJSONIsMatchSchema( - BuiltinContext{}, - []*ast.Term{ast.NewTerm(tc.document), ast.NewTerm(tc.schema)}, - func(term *ast.Term) error { - result = term.Value - return nil - }, - ) - - if tc.err && err == nil { - t.Errorf("Unexpected schema validation, expected error, got nil") - return - } - if !tc.err && err != nil { - t.Errorf("Unexpected schema validation, expected nil, got error: %s", err) - return - } - if tc.result.Compare(result) != 0 { - t.Errorf("Unexpected schema validation, expected result %s, got result %s", tc.result.String(), result.String()) - return - } - }) - } -}