import type Ajv from ".." ; import type { ValidateFunction } from ".." ; import _Ajv from "./ajv" ; import chai from "./chai" ; const should = chai . should ( ) ; describe ( "Validation errors" , ( ) => { let ajv , ajvJP , fullAjv ; beforeEach ( ( ) => { createInstances ( ) ; } ) ; function createInstances ( ) { const opts = { loopRequired : 21 , strictTypes : false , strictTuples : false , } ; ajv = new _Ajv ( opts ) ; ajvJP = new _Ajv ( opts ) ; fullAjv = new _Ajv ( { ... opts , allErrors : true , verbose : true , jsPropertySyntax : true , logger : false , } ) ; } it ( "error should include instancePath" , ( ) => { const schema = { properties : { foo : { type : "number" } , } , } ; testSchema1 ( schema ) ; } ) ; it ( '"refs" error should include instancePath' , ( ) => { const schema = { definitions : { num : { type : "number" } , } , properties : { foo : { $ref : "#/definitions/num" } , } , } ; testSchema1 ( schema , "#/definitions/num" ) ; } ) ; describe ( '"additionalProperties" errors' , ( ) => { it ( "should NOT include property in instancePath" , ( ) => { testAdditional ( ) ; } ) ; function testAdditional ( ) { const schema = { properties : { foo : { } , bar : { } , } , additionalProperties : false , } ; const data = { foo : 1 , bar : 2 } , invalidData = { foo : 1 , bar : 2 , baz : 3 , quux : 4 } ; const msg = "must NOT have additional properties" ; const validate = ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData ) ; shouldBeError ( validate . errors ?. [ 0 ] , "additionalProperties" , "#/additionalProperties" , "" , msg , { additionalProperty : "baz" , } ) ; const validateJP = ajvJP . compile ( schema ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "additionalProperties" , "#/additionalProperties" , "" , msg , { additionalProperty : "baz" } ) ; const fullValidate = fullAjv . compile ( schema ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData , 2 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "additionalProperties" , "#/additionalProperties" , "" , msg , { additionalProperty : "baz" } ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "additionalProperties" , "#/additionalProperties" , "" , msg , { additionalProperty : "quux" } ) ; } } ) ; describe ( 'errors when "additionalProperties" is schema' , ( ) => { it ( "should NOT include property in instancePath" , ( ) => { testAdditionalIsSchema ( ) ; } ) ; function testAdditionalIsSchema ( ) { const schema = { properties : { foo : { type : "integer" } , bar : { type : "integer" } , } , additionalProperties : { type : "object" , properties : { quux : { type : "string" } , } , } , } ; const data = { foo : 1 , bar : 2 , baz : { quux : "abc" } } , invalidData = { foo : 1 , bar : 2 , baz : { quux : 3 } , boo : { quux : 4 } } ; const schPath = "#/additionalProperties/properties/quux/type" ; const validate = ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData ) ; shouldBeError ( validate . errors ?. [ 0 ] , "type" , schPath , "/baz/quux" , "must be string" , { type : "string" , } ) ; const validateJP = ajvJP . compile ( schema ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "type" , schPath , "/baz/quux" , "must be string" , { type : "string" , } ) ; const fullValidate = fullAjv . compile ( schema ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData , 2 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "type" , schPath , "['baz'].quux" , "must be string" , { type : "string" , } ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "type" , schPath , "['boo'].quux" , "must be string" , { type : "string" , } ) ; } } ) ; describe ( '"required" errors' , ( ) => { it ( "should NOT include missing property in instancePath" , ( ) => { testRequired ( ) ; } ) ; function testRequired ( ) { const schema = { required : [ "foo" , "bar" , "baz" ] , } ; _testRequired ( schema , "#" ) ; } it ( "large data/schemas" , ( ) => { testRequiredLargeSchema ( ) ; } ) ; function testRequiredLargeSchema ( ) { let schema = { required : [ ] } ; const data = { } , invalidData1 = { } , invalidData2 = { } ; for ( let i = 0 ; i < 100 ; i ++ ) { schema . required . push ( "" + i ) ; data [ i ] = invalidData1 [ i ] = invalidData2 [ i ] = i ; } delete invalidData1 [ 1 ] ; delete invalidData2 [ 2 ] ; delete invalidData2 [ 98 ] ; test ( ) ; schema = { anyOf : [ schema ] } ; test ( 1 , "#/anyOf/0" ) ; function test ( extraErrors = 0 , schemaPathPrefix = "#" ) { const schPath = schemaPathPrefix + "/required" ; const validate = ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData1 , 1 + extraErrors ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "1" ) , { missingProperty : "1" , } ) ; shouldBeInvalid ( validate , invalidData2 , 1 + extraErrors ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "2" ) , { missingProperty : "2" , } ) ; const validateJP = ajvJP . compile ( schema ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData1 , 1 + extraErrors ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "1" ) , { missingProperty : "1" , } ) ; shouldBeInvalid ( validateJP , invalidData2 , 1 + extraErrors ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "2" ) , { missingProperty : "2" , } ) ; const fullValidate = fullAjv . compile ( schema ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData1 , 1 + extraErrors ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "1" ) , { missingProperty : "1" , } ) ; shouldBeInvalid ( fullValidate , invalidData2 , 2 + extraErrors ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "2" ) , { missingProperty : "2" , } ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "required" , schPath , "" , requiredMsg ( "98" ) , { missingProperty : "98" , } ) ; } } it ( 'with "properties"' , ( ) => { testRequiredAndProperties ( ) ; } ) ; function testRequiredAndProperties ( ) { const schema = { properties : { foo : { type : "number" } , bar : { type : "number" } , baz : { type : "number" } , } , required : [ "foo" , "bar" , "baz" ] , } ; _testRequired ( schema ) ; } it ( 'in "anyOf"' , ( ) => { testRequiredInAnyOf ( ) ; } ) ; function testRequiredInAnyOf ( ) { const schema = { anyOf : [ { required : [ "foo" , "bar" , "baz" ] } ] , } ; _testRequired ( schema , "#/anyOf/0" , 1 ) ; } it ( "should not validate required twice in large schemas with loopRequired option" , ( ) => { ajv = new _Ajv ( { loopRequired : 1 , allErrors : true } ) ; const schema = { type : "object" , properties : { foo : { type : "integer" } , bar : { type : "integer" } , } , required : [ "foo" , "bar" ] , } ; const validate = ajv . compile ( schema ) ; validate ( { } ) . should . equal ( false ) ; validate . errors ?. should . have . length ( 2 ) ; } ) ; it ( "should not validate required twice with $data ref" , ( ) => { ajv = new _Ajv ( { $data : true , allErrors : true } ) ; const schema = { type : "object" , properties : { foo : { type : "integer" } , bar : { type : "integer" } , } , required : { $data : "0/requiredProperties" } , } ; const validate = ajv . compile ( schema ) ; validate ( { requiredProperties : [ "foo" , "bar" ] } ) . should . equal ( false ) ; validate . errors ?. should . have . length ( 2 ) ; } ) ; it ( "should show different error when required is $data of incorrect type" , ( ) => { test ( new _Ajv ( { $data : true } ) ) ; test ( new _Ajv ( { $data : true , allErrors : true } ) ) ; function test ( _ajv ) { const schema = { type : "object" , required : { $data : "0/req" } , properties : { req : { } , foo : { } , bar : { } , } , } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , { req : [ "foo" , "bar" ] , foo : 1 , bar : 2 } ) ; shouldBeInvalid ( validate , { req : [ "foo" , "bar" ] , foo : 1 } ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , "#/required" , "" , "must have required property 'bar'" , { missingProperty : "bar" } ) ; shouldBeInvalid ( validate , { req : "invalid" } ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , "#/required" , "" , '"required" keyword must be array ($data)' , { } ) ; } } ) ; it ( "should include missing property with ownProperties option (issue #1493)" , ( ) => { test ( new _Ajv ( ) ) ; test ( new _Ajv ( { ownProperties : true } ) ) ; function test ( _ajv ) { const schema = { type : "object" , required : [ "a" ] , properties : { a : { type : "string" } , } , } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , { a : "abc" } ) ; shouldBeInvalid ( validate , { } ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , "#/required" , "" , "must have required property 'a'" , { missingProperty : "a" } ) ; } } ) ; } ) ; describe ( '"dependencies" errors' , ( ) => { it ( "should NOT include missing property in instancePath" , ( ) => { testDependencies ( ) ; } ) ; function testDependencies ( ) { const schema = { dependencies : { a : [ "foo" , "bar" , "baz" ] , } , } ; const data = { a : 0 , foo : 1 , bar : 2 , baz : 3 } , invalidData1 = { a : 0 , foo : 1 , baz : 3 } , invalidData2 = { a : 0 , bar : 2 } ; const msg = "must have properties foo, bar, baz when property a is present" ; const validate = ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData1 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "bar" ) ) ; shouldBeInvalid ( validate , invalidData2 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "foo" ) ) ; const validateJP = ajvJP . compile ( schema ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData1 ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "bar" ) ) ; shouldBeInvalid ( validateJP , invalidData2 ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "foo" ) ) ; const fullValidate = fullAjv . compile ( schema ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData1 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "bar" ) ) ; shouldBeInvalid ( fullValidate , invalidData2 , 2 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "foo" ) ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "dependencies" , "#/dependencies" , "" , msg , params ( "baz" ) ) ; function params ( missing ) { const p = { property : "a" , deps : "foo, bar, baz" , depsCount : 3 , missingProperty : missing , } ; return p ; } } } ) ; function _testRequired ( schema , schemaPathPrefix = "#" , extraErrors = 0 ) { const schPath = schemaPathPrefix + "/required" ; const data = { foo : 1 , bar : 2 , baz : 3 } , invalidData1 = { foo : 1 , baz : 3 } , invalidData2 = { bar : 2 } ; const validate = ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData1 , 1 + extraErrors ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "bar" ) , { missingProperty : "bar" , } ) ; shouldBeInvalid ( validate , invalidData2 , 1 + extraErrors ) ; shouldBeError ( validate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "foo" ) , { missingProperty : "foo" , } ) ; const validateJP = ajvJP . compile ( schema ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData1 , 1 + extraErrors ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "bar" ) , { missingProperty : "bar" , } ) ; shouldBeInvalid ( validateJP , invalidData2 , 1 + extraErrors ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "foo" ) , { missingProperty : "foo" , } ) ; const fullValidate = fullAjv . compile ( schema ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData1 , 1 + extraErrors ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "bar" ) , { missingProperty : "bar" , } ) ; shouldBeInvalid ( fullValidate , invalidData2 , 2 + extraErrors ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "required" , schPath , "" , requiredMsg ( "foo" ) , { missingProperty : "foo" , } ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "required" , schPath , "" , requiredMsg ( "baz" ) , { missingProperty : "baz" , } ) ; } function requiredMsg ( prop ) { return `must have required property '${ prop }'` ; } it ( '"items" errors should include item index without quotes in instancePath (#48)' , ( ) => { const schema1 = { $id : "schema1" , type : "array" , items : { type : "integer" , minimum : 10 , } , } ; const data = [ 10 , 11 , 12 ] , invalidData1 = [ 1 , 10 ] , invalidData2 = [ 10 , 9 , 11 , 8 , 12 ] ; let validate = ajv . compile ( schema1 ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData1 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "/0" , "must be >= 10" ) ; shouldBeInvalid ( validate , invalidData2 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "/1" , "must be >= 10" ) ; const validateJP = ajvJP . compile ( schema1 ) ; shouldBeValid ( validateJP , data ) ; shouldBeInvalid ( validateJP , invalidData1 ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "/0" , "must be >= 10" ) ; shouldBeInvalid ( validateJP , invalidData2 ) ; shouldBeError ( validateJP . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "/1" , "must be >= 10" ) ; const fullValidate = fullAjv . compile ( schema1 ) ; shouldBeValid ( fullValidate , data ) ; shouldBeInvalid ( fullValidate , invalidData1 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "[0]" , "must be >= 10" ) ; shouldBeInvalid ( fullValidate , invalidData2 , 2 ) ; shouldBeError ( fullValidate . errors ?. [ 0 ] , "minimum" , "#/items/minimum" , "[1]" , "must be >= 10" ) ; shouldBeError ( fullValidate . errors ?. [ 1 ] , "minimum" , "#/items/minimum" , "[3]" , "must be >= 10" ) ; const schema2 = { $id : "schema2" , type : "array" , items : [ { minimum : 10 } , { minimum : 9 } , { minimum : 12 } ] , } ; validate = ajv . compile ( schema2 ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData1 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "minimum" , "#/items/0/minimum" , "/0" , "must be >= 10" ) ; shouldBeInvalid ( validate , invalidData2 ) ; shouldBeError ( validate . errors ?. [ 0 ] , "minimum" , "#/items/2/minimum" , "/2" , "must be >= 12" ) ; } ) ; it ( "should have correct schema path for additionalItems" , ( ) => { const schema = { type : "array" , items : [ { type : "integer" } , { type : "integer" } ] , minItems : 2 , additionalItems : false , } ; const data = [ 1 , 2 ] ; const invalidData = [ 1 , 2 , 3 ] ; test ( ajv ) ; test ( ajvJP ) ; test ( fullAjv ) ; function test ( _ajv ) { const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData ) ; shouldBeError ( validate . errors ?. [ 0 ] , "additionalItems" , "#/additionalItems" , "" , "must NOT have more than 2 items" ) ; } } ) ; describe ( '"propertyNames" errors' , ( ) => { it ( "should add propertyName to errors" , ( ) => { const schema = { type : "object" , propertyNames : { pattern : "bar" } , } ; const data = { bar : { } , "bar.baz@email.example.com" : { } , } ; const invalidData = { bar : { } , "bar.baz@email.example.com" : { } , foo : { } , quux : { } , } ; test ( ajv , 2 ) ; test ( ajvJP , 2 ) ; test ( fullAjv , 4 ) ; function test ( _ajv , numErrors ) { const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData , numErrors ) ; shouldBeError ( validate . errors ?. [ 0 ] , "pattern" , "#/propertyNames/pattern" , "" , 'must match pattern "bar"' ) ; shouldBeError ( validate . errors ?. [ 1 ] , "propertyNames" , "#/propertyNames" , "" , "property name must be valid" ) ; if ( numErrors === 4 ) { shouldBeError ( validate . errors ?. [ 2 ] , "pattern" , "#/propertyNames/pattern" , "" , 'must match pattern "bar"' ) ; shouldBeError ( validate . errors ?. [ 3 ] , "propertyNames" , "#/propertyNames" , "" , "property name must be valid" ) ; } } } ) ; } ) ; describe ( "oneOf errors" , ( ) => { it ( "should have errors from inner schemas" , ( ) => { const schema = { oneOf : [ { type : "number" } , { type : "integer" } ] , } ; test ( ajv ) ; test ( fullAjv ) ; function test ( _ajv ) { const validate = _ajv . compile ( schema ) ; validate ( "foo" ) . should . equal ( false ) ; validate . errors . length . should . equal ( 3 ) ; validate ( 1 ) . should . equal ( false ) ; validate . errors . length . should . equal ( 1 ) ; validate ( 1.5 ) . should . equal ( true ) ; } } ) ; it ( "should return passing schemas in error params" , ( ) => { const schema = { oneOf : [ { type : "number" } , { type : "integer" } , { const : 1.5 } ] , } ; test ( ajv ) ; test ( fullAjv ) ; function test ( _ajv ) { const validate = _ajv . compile ( schema ) ; validate ( 1 ) . should . equal ( false ) ; let err = validate . errors . pop ( ) ; err . keyword . should . equal ( "oneOf" ) ; err . params . should . eql ( { passingSchemas : [ 0 , 1 ] } ) ; validate ( 1.5 ) . should . equal ( false ) ; err = validate . errors . pop ( ) ; err . keyword . should . equal ( "oneOf" ) ; err . params . should . eql ( { passingSchemas : [ 0 , 2 ] } ) ; validate ( 2.5 ) . should . equal ( true ) ; validate ( "foo" ) . should . equal ( false ) ; err = validate . errors . pop ( ) ; err . keyword . should . equal ( "oneOf" ) ; err . params . should . eql ( { passingSchemas : null } ) ; } } ) ; } ) ; describe ( "anyOf errors" , ( ) => { it ( "should have errors from inner schemas" , ( ) => { const schema = { anyOf : [ { type : "number" } , { type : "integer" } ] , } ; test ( ajv ) ; test ( fullAjv ) ; function test ( _ajv ) { const validate = _ajv . compile ( schema ) ; validate ( "foo" ) . should . equal ( false ) ; validate . errors . length . should . equal ( 3 ) ; validate ( 1 ) . should . equal ( true ) ; validate ( 1.5 ) . should . equal ( true ) ; } } ) ; } ) ; describe ( "type errors" , ( ) => { describe ( "integer" , ( ) => { it ( "should have only one error in {allErrors: false} mode" , ( ) => { test ( ajv ) ; } ) ; it ( "should return all errors in {allErrors: true} mode" , ( ) => { test ( fullAjv , 2 ) ; } ) ; function test ( _ajv , numErrors ? ) { const schema = { type : "integer" , minimum : 5 , } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , 5 ) ; shouldBeInvalid ( validate , 5.5 ) ; shouldBeInvalid ( validate , 4 ) ; shouldBeInvalid ( validate , "4" ) ; shouldBeInvalid ( validate , 4.5 , numErrors ) ; } } ) ; describe ( "keyword for another type" , ( ) => { it ( "should have only one error in {allErrors: false} mode" , ( ) => { test ( ajv ) ; } ) ; it ( "should return all errors in {allErrors: true} mode" , ( ) => { test ( fullAjv , 2 ) ; } ) ; function test ( _ajv , numErrors ? ) { const schema = { type : "array" , minItems : 2 , minimum : 5 , } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , [ 1 , 2 ] ) ; shouldBeInvalid ( validate , [ 1 ] ) ; shouldBeInvalid ( validate , 5 ) ; shouldBeInvalid ( validate , 4 , numErrors ) ; } } ) ; describe ( "array of types" , ( ) => { it ( "should have only one error in {allErrors: false} mode" , ( ) => { test ( ajv ) ; } ) ; it ( "should return all errors in {allErrors: true} mode" , ( ) => { test ( fullAjv , 2 ) ; } ) ; function test ( _ajv , numErrors ? ) { const schema = { type : [ "array" , "object" ] , minItems : 2 , minProperties : 2 , minimum : 5 , } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , [ 1 , 2 ] ) ; shouldBeValid ( validate , { foo : 1 , bar : 2 } ) ; shouldBeInvalid ( validate , [ 1 ] ) ; shouldBeInvalid ( validate , { foo : 1 } ) ; shouldBeInvalid ( validate , 5 ) ; shouldBeInvalid ( validate , 4 , numErrors ) ; } } ) ; } ) ; describe ( "exclusiveMaximum/Minimum errors" , ( ) => { it ( "should include limits in error message" , ( ) => { const schema = { type : "integer" , exclusiveMinimum : 2 , exclusiveMaximum : 5 , } ; [ ajv , fullAjv ] . forEach ( ( _ajv ) => { const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , 3 ) ; shouldBeValid ( validate , 4 ) ; shouldBeInvalid ( validate , 2 ) ; testError ( "exclusiveMinimum" , "must be > 2" , { comparison : ">" , limit : 2 , } ) ; shouldBeInvalid ( validate , 5 ) ; testError ( "exclusiveMaximum" , "must be < 5" , { comparison : "<" , limit : 5 , } ) ; function testError ( keyword , message , params ) { const err = validate . errors ?. [ 0 ] ; shouldBeError ( err , keyword , "#/" + keyword , "" , message , params ) ; } } ) ; } ) ; it ( "should include limits in error message with $data" , ( ) => { const schema = { type : "object" , properties : { smaller : { type : "number" , exclusiveMaximum : { $data : "1/larger" } , } , larger : { type : "number" } , } , } ; ajv = new _Ajv ( { $data : true } ) ; fullAjv = new _Ajv ( { $data : true , allErrors : true , verbose : true , jsPropertySyntax : true , logger : false , } ) ; [ ajv , fullAjv ] . forEach ( ( _ajv ) => { const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , { smaller : 2 , larger : 4 } ) ; shouldBeValid ( validate , { smaller : 3 , larger : 4 } ) ; shouldBeInvalid ( validate , { smaller : 4 , larger : 4 } ) ; testError ( ) ; shouldBeInvalid ( validate , { smaller : 5 , larger : 4 } ) ; testError ( ) ; function testError ( ) { const err = validate . errors ?. [ 0 ] ; shouldBeError ( err , "exclusiveMaximum" , "#/properties/smaller/exclusiveMaximum" , _ajv . opts . jsPropertySyntax ? ".smaller" : "/smaller" , "must be < 4" , { comparison : "<" , limit : 4 } ) ; } } ) ; } ) ; } ) ; describe ( "if/then/else errors" , ( ) => { let validate , numErrors ; it ( "if/then/else should include failing keyword in message and params" , ( ) => { const schema = { type : "number" , if : { maximum : 10 } , then : { multipleOf : 2 } , else : { multipleOf : 5 } , } ; [ ajv , fullAjv ] . forEach ( ( _ajv ) => { prepareTest ( _ajv , schema ) ; shouldBeValid ( validate , 8 ) ; shouldBeValid ( validate , 15 ) ; shouldBeInvalid ( validate , 7 , numErrors ) ; testIfError ( "then" , 2 ) ; shouldBeInvalid ( validate , 17 , numErrors ) ; testIfError ( "else" , 5 ) ; } ) ; } ) ; it ( "if/then should include failing keyword in message and params" , ( ) => { const schema = { type : "number" , if : { maximum : 10 } , then : { multipleOf : 2 } , } ; [ ajv , fullAjv ] . forEach ( ( _ajv ) => { prepareTest ( _ajv , schema ) ; shouldBeValid ( validate , 8 ) ; shouldBeValid ( validate , 11 ) ; shouldBeValid ( validate , 12 ) ; shouldBeInvalid ( validate , 7 , numErrors ) ; testIfError ( "then" , 2 ) ; } ) ; } ) ; it ( "if/else should include failing keyword in message and params" , ( ) => { const schema = { type : "number" , if : { maximum : 10 } , else : { multipleOf : 5 } , } ; [ ajv , fullAjv ] . forEach ( ( _ajv ) => { prepareTest ( _ajv , schema ) ; shouldBeValid ( validate , 7 ) ; shouldBeValid ( validate , 8 ) ; shouldBeValid ( validate , 15 ) ; shouldBeInvalid ( validate , 17 , numErrors ) ; testIfError ( "else" , 5 ) ; } ) ; } ) ; function prepareTest ( _ajv , schema ) { validate = _ajv . compile ( schema ) ; numErrors = _ajv . opts . allErrors ? 2 : 1 ; } function testIfError ( ifClause , multipleOf ) { let err = validate . errors ?. [ 0 ] ; shouldBeError ( err , "multipleOf" , "#/" + ifClause + "/multipleOf" , "" , "must be multiple of " + multipleOf , { multipleOf : multipleOf } ) ; if ( numErrors === 2 ) { err = validate . errors ?. [ 1 ] ; shouldBeError ( err , "if" , "#/if" , "" , 'must match "' + ifClause + '" schema' , { failingKeyword : ifClause , } ) ; } } } ) ; describe ( "uniqueItems errors" , ( ) => { it ( "should not return uniqueItems error when non-unique items are of a different type than required" , ( ) => { const schema = { type : "array" , items : { type : "number" } , uniqueItems : true , } ; [ ajvJP , fullAjv ] . forEach ( ( _ajv ) => { const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , [ 1 , 2 , 3 ] ) ; shouldBeInvalid ( validate , [ 1 , 2 , 2 ] ) ; shouldBeError ( validate . errors ?. [ 0 ] , "uniqueItems" , "#/uniqueItems" , "" , "must NOT have duplicate items (items ## 2 and 1 are identical)" , { i : 1 , j : 2 } ) ; const expectedErrors = _ajv . opts . allErrors ? 2 : 1 ; shouldBeInvalid ( validate , [ 1 , "2" , "2" , 2 ] , expectedErrors ) ; testTypeError ( 0 , _ajv . opts . jsPropertySyntax ? "[1]" : "/1" ) ; if ( expectedErrors === 2 ) testTypeError ( 1 , _ajv . opts . jsPropertySyntax ? "[2]" : "/2" ) ; function testTypeError ( i , instancePath ) { const err = validate . errors ?. [ i ] ; shouldBeError ( err , "type" , "#/items/type" , instancePath , "must be number" ) ; } } ) ; } ) ; } ) ; function testSchema1 ( schema , schemaPathPrefix = "#/properties/foo" ) { _testSchema1 ( ajv , schema , schemaPathPrefix ) ; _testSchema1 ( ajvJP , schema , schemaPathPrefix ) ; _testSchema1 ( fullAjv , schema , schemaPathPrefix ) ; } function _testSchema1 ( _ajv , schema , schemaPathPrefix ) { const schPath = schemaPathPrefix + "/type" ; const data = { foo : 1 } , invalidData = { foo : "bar" } ; const validate = _ajv . compile ( schema ) ; shouldBeValid ( validate , data ) ; shouldBeInvalid ( validate , invalidData ) ; shouldBeError ( validate . errors ?. [ 0 ] , "type" , schPath , _ajv . opts . jsPropertySyntax ? ".foo" : "/foo" ) ; } function shouldBeValid ( validate , data ) { validate ( data ) . should . equal ( true ) ; should . equal ( validate . errors , null ) ; } function shouldBeInvalid ( validate , data , numErrors = 1 ) { validate ( data ) . should . equal ( false ) ; should . equal ( validate . errors . length , numErrors ) ; } function shouldBeError ( error , keyword , schemaPath , instancePath , message ? , params ? ) { error . keyword . should . equal ( keyword ) ; error . schemaPath . should . equal ( schemaPath ) ; error . instancePath . should . equal ( instancePath ) ; error . message . should . be . a ( "string" ) ; if ( message !== undefined ) error . message . should . equal ( message ) ; if ( params !== undefined ) error . params . should . eql ( params ) ; } } ) ;