import type { JSONSchemaType } from "../.." ; import _Ajv from "../ajv" ; import chai from "../chai" ; const should = chai . should ( ) ; describe ( "strict mode" , ( ) => { describe ( '"additionalItems" without "items"' , testStrictMode ( { type : "array" , additionalItems : false } , /additionalItems/ ) ) ; describe ( '"if" without "then" and "else"' , testStrictMode ( { if : true } , /if.*then.*else/ ) ) ; describe ( '"then" without "if"' , testStrictMode ( { then : true } , /then.*if/ ) ) ; describe ( '"else" without "if"' , testStrictMode ( { else : true } , /else.*if/ ) ) ; describe ( '"properties" matching "patternProperties"' , testStrictMode ( { type : "object" , properties : { foo : false } , patternProperties : { foo : false } , } , /property.*pattern/ ) ) ; describe ( 'option allowMatchingProperties to allow "properties" matching "patternProperties"' , ( ) => { it ( "should NOT throw an error or log a warning" , ( ) => { const output = { } ; const ajv = new _Ajv ( { allowMatchingProperties : true , logger : getLogger ( output ) , } ) ; const schema = { type : "object" , properties : { foo : false } , patternProperties : { foo : false } , } ; ajv . compile ( schema ) ; should . not . exist ( output . warning ) ; } ) ; } ) ; describe ( "strictTypes option" , ( ) => { const ajv = new _Ajv ( { strictTypes : true } ) ; const ajvUT = new _Ajv ( { strictTypes : true , allowUnionTypes : true } ) ; describe ( "multiple/union types" , ( ) => { it ( "should prohibit multiple types" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : [ "number" , "string" ] } ) ; } , /use allowUnionTypes to allow union type/ ) ; } ) ; it ( "should allow multiple types with option allowUnionTypes" , ( ) => { should . not . throw ( ( ) => { ajvUT . compile ( { type : [ "number" , "string" ] } ) ; } ) ; } ) ; it ( "should allow nullable" , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : [ "number" , "null" ] } ) ; ajv . compile ( { type : [ "number" ] , nullable : true } ) ; } ) ; } ) ; } ) ; describe ( "contradictory types" , ( ) => { it ( "should prohibit contradictory types" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "object" , anyOf : [ { type : "object" } , { type : "array" } ] , } ) ; } , /type "array" not allowed by context "object"/ ) ; } ) ; it ( "should allow narrowing types" , ( ) => { should . not . throw ( ( ) => { ajvUT . compile ( { type : [ "object" , "array" ] , anyOf : [ { type : "object" } , { type : "array" } ] , } ) ; } ) ; } ) ; it ( 'should allow "integer" in "number" context' , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : "number" , anyOf : [ { type : "integer" } ] , } ) ; } ) ; } ) ; it ( 'should prohibit "number" in "integer" context' , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "integer" , anyOf : [ { type : "number" } ] , } ) ; } , /type "number" not allowed by context "integer"/ ) ; } ) ; } ) ; describe ( "applicable types" , ( ) => { it ( "should prohibit keywords without applicable types" , ( ) => { should . throw ( ( ) => { ajv . compile ( { properties : { foo : { type : "number" , minimum : 0 } , } , } ) ; } , /missing type "object" for keyword "properties"/ ) ; should . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { minimum : 0 } , } , } ) ; } , /missing type "number" for keyword "minimum"/ ) ; } ) ; it ( "should allow keywords with applicable types" , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { type : "number" , minimum : 0 } , } , } ) ; } ) ; } ) ; it ( "should allow keywords with applicable type in parent schema" , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : "object" , anyOf : [ { properties : { foo : { type : "number" } , } , } , { properties : { bar : { type : "string" } , } , } , ] , } ) ; } ) ; } ) ; } ) ; describe ( "propertyNames" , ( ) => { it ( 'should set default data type "string"' , ( ) => { ajv . compile ( { type : "object" , propertyNames : { maxLength : 5 } , } ) ; ajv . compile ( { type : "object" , propertyNames : { type : "string" , maxLength : 5 } , } ) ; should . throw ( ( ) => { ajv . compile ( { type : "object" , propertyNames : { type : "number" } , } ) ; } , /type "number" not allowed by context/ ) ; } ) ; } ) ; } ) ; describe ( "option strictTuples" , ( ) => { const ajv = new _Ajv ( { strictTuples : true } ) ; type MyTuple = [ string , number ] ; it ( "should prohibit unconstrained tuples" , ( ) => { const schema1 = { type : "array" , items : [ { type : "string" } , { type : "number" } ] , minItems : 2 , additionalItems : false , } ; should . not . throw ( ( ) => { ajv . compile ( schema1 ) ; } ) ; const schema2 = { type : "array" , items : [ { type : "string" } , { type : "number" } ] , minItems : 2 , maxItems : 2 , } ; should . not . throw ( ( ) => { ajv . compile ( schema2 ) ; } ) ; const badSchema1 = { type : "object" , properties : { test : { type : "array" , items : [ { type : "string" } , { type : "number" } ] , additionalItems : false , } , } , } ; should . throw ( ( ) => { ajv . compile ( badSchema1 ) ; } , / minItems or maxItems\/additionalItems are not specified or different at path "#\/properties\/test"/ ) ; const badSchema2 = { type : "object" , properties : { test : { type : "array" , items : [ { type : "string" } , { type : "number" } ] , minItems : 2 , } , } , } ; should . throw ( ( ) => { ajv . compile ( badSchema2 ) ; } , / minItems or maxItems\/additionalItems are not specified or different at path "#\/properties\/test"/ ) ; const badSchema3 = { type : "object" , properties : { test : { type : "array" , items : [ { type : "string" } , { type : "number" } ] , minItems : 2 , maxItems : 3 , } , } , } ; should . throw ( ( ) => { ajv . compile ( badSchema3 ) ; } , / minItems or maxItems\/additionalItems are not specified or different at path "#\/properties\/test"/ ) ; } ) ; } ) ; describe ( "strictRequired option" , ( ) => { const ajv = new _Ajv ( { strictRequired : true } ) ; describe ( "base case" , ( ) => { const schema = { type : "object" , properties : { notTest : { type : "string" , } , } , required : [ "test" ] , } ; it ( "should prohibit with strictRequired: true" , ( ) => { should . throw ( ( ) => ajv . compile ( schema ) , 'strict mode: required property "test" is not defined at "#" (strictRequired)' ) ; } ) ; it ( "should NOT prohibit when strictRequired is not set" , ( ) => { should . not . throw ( ( ) => new _Ajv ( ) . compile ( schema ) ) ; } ) ; } ) ; it ( "should prohibit in second level of a schema" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { test : { type : "object" , properties : { } , required : [ "keyname" ] , } , } , } ) ; } , 'strict mode: required property "keyname" is not defined at "#/properties/test" (strictRequired)' ) ; } ) ; it . skip ( "should not throw with a same level if then" , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { } } , if : { required : [ "foo" ] } , then : { properties : { bar : { type : "boolean" } } } , } ) ; } ) ; } ) ; it ( "should throw if a required property exists in a parent object but not in the subschema that the require keyword references" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { type : "object" , required : "foo" , properties : { test : { type : "integer" , } , } , } , } , } ) ; } ) ; } ) ; it ( "should throw if property exists in parent but not in actual object required references" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { type : "object" , required : "foo" , properties : { test : { type : "number" , } , } , } , } , } ) ; } ) ; } ) ; it . skip ( "should not throw because all referenced properties are defined" , ( ) => { should . not . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { } , bar : { } } , allOf : [ { allOf : [ { if : { required : [ "foo" ] } , then : { required : [ "bar" ] } , } , ] , } , ] , } ) ; } ) ; } ) ; it ( "should throw because baz does not exist as a property" , ( ) => { should . throw ( ( ) => { ajv . compile ( { type : "object" , properties : { foo : { } , bar : { } } , allOf : [ { allOf : [ { if : { required : [ "bar" ] } , then : { required : [ "baz" ] } , } , ] , } , ] , } ) ; } ) ; } ) ; } ) ; } ) ; function testStrictMode ( schema , logPattern ) { return ( ) => { describe ( "strict = false" , ( ) => { it ( "should NOT throw an error or log a warning" , ( ) => { const output = { } ; const ajv = new _Ajv ( { strict : false , logger : getLogger ( output ) , } ) ; ajv . compile ( schema ) ; should . not . exist ( output . warning ) ; } ) ; } ) ; describe ( "strict = true or undefined" , ( ) => { it ( "should throw an error" , ( ) => { test ( new _Ajv ( { strict : true } ) ) ; test ( new _Ajv ( ) ) ; function test ( ajv ) { should . throw ( ( ) => { ajv . compile ( schema ) ; } , logPattern ) ; } } ) ; } ) ; describe ( 'strict = "log"' , ( ) => { it ( "should log a warning" , ( ) => { const output = { } ; const ajv = new _Ajv ( { strict : "log" , logger : getLogger ( output ) , } ) ; ajv . compile ( schema ) ; output . warning . should . match ( logPattern ) ; } ) ; } ) ; } ; } function getLogger ( output ) { return { log ( ) { throw new Error ( "log should not be called" ) ; } , warn ( msg ) { output . warning = msg ; } , error ( ) { throw new Error ( "error should not be called" ) ; } , } ; }