├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .travis.yml ├── README.md ├── package-lock.json ├── package.json ├── src ├── index.js ├── merge-validation-options.js ├── property-rules.js ├── scaffold.js ├── validate.js └── validators │ ├── additionalItems.js │ ├── additionalProperties.js │ ├── allOf.js │ ├── anyOf.js │ ├── between.js │ ├── const.js │ ├── contains.js │ ├── dependencies.js │ ├── enum.js │ ├── exclusiveMaximum.js │ ├── exclusiveMinimum.js │ ├── items.js │ ├── maxItems.js │ ├── maxLength.js │ ├── maxProperties.js │ ├── maximum.js │ ├── minItems.js │ ├── minLength.js │ ├── minProperties.js │ ├── minimum.js │ ├── multipleOf.js │ ├── noParamsRequired.js │ ├── not.js │ ├── oneOf.js │ ├── pattern.js │ ├── patternProperties.js │ ├── propertyNames.js │ ├── required.js │ ├── type.js │ ├── typeArray.js │ └── uniqueItems.js └── test ├── .eslintrc ├── fixtures └── schemas │ ├── additional.json │ ├── additionalItems.json │ ├── additionalProperties.json │ ├── additionalWithPattern.json │ ├── advanced.json │ ├── allOf.json │ ├── anyOf.json │ ├── basic.json │ ├── boolean_schema.json │ ├── complex.json │ ├── complex2.json │ ├── complex3.json │ ├── const.json │ ├── contains.json │ ├── cosmicrealms.json │ ├── default.json │ ├── definitions.json │ ├── dependencies.json │ ├── enum.json │ ├── exclusiveMaximum.json │ ├── exclusiveMinimum.json │ ├── items.json │ ├── maxItems.json │ ├── maxLength.json │ ├── maxProperties.json │ ├── maximum.json │ ├── medium.json │ ├── minItems.json │ ├── minLength.json │ ├── minProperties.json │ ├── minimum.json │ ├── multipleOf.json │ ├── not.json │ ├── oneOf.json │ ├── optional │ ├── bignum.json │ ├── ecmascript-regex.json │ ├── format.json │ └── zeroTerminatedFloats.json │ ├── pattern.json │ ├── patternProperties.json │ ├── properties.json │ ├── propertyNames.json │ ├── ref.json │ ├── refRemote.json │ ├── required.json │ ├── type.json │ └── uniqueItems.json └── specs ├── index.spec.js ├── merge-validation-options.spec.js ├── scaffold.spec.js ├── schemas.spec.js └── validators ├── additionalItems.spec.js ├── allOf.spec.js ├── anyOf.spec.js ├── items.spec.js ├── multipleOf.spec.js ├── not.spec.js ├── oneOf.spec.js └── patternProperties.spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.yml] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'standard', 3 | rules: { 4 | 'space-before-function-paren': ['error', { 5 | anonymous: 'ignore', 6 | named: 'ignore', 7 | asyncArrow: 'ignore' 8 | }] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | after_success: npm run coverage 3 | node_js: 4 | - '8' 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-vuelidate-jsonschema", 3 | "version": "0.13.4", 4 | "description": "Create validation definitions for vuelidate based on json schema", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "eslint": "eslint src test", 8 | "test": "npm run eslint && nyc --reporter=html --reporter=text mocha test/specs", 9 | "develop": "mocha test/specs --recursive --watch", 10 | "coverage": "nyc report --reporter=text-lcov | coveralls" 11 | }, 12 | "author": "Martin Hansen", 13 | "license": "MIT", 14 | "keywords": [ 15 | "vue", 16 | "vuelidate", 17 | "validation", 18 | "json", 19 | "jsonschema", 20 | "schema" 21 | ], 22 | "directories": { 23 | "lib": "src", 24 | "test": "test" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git+https://github.com/mokkabonna/vue-vuelidate-jsonschema.git" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/mokkabonna/vue-vuelidate-jsonschema/issues" 32 | }, 33 | "homepage": "https://github.com/mokkabonna/vue-vuelidate-jsonschema#readme", 34 | "devDependencies": { 35 | "chai": "^4.1.2", 36 | "coveralls": "^3.0.0", 37 | "eslint": "^4.8.0", 38 | "eslint-config": "^0.3.0", 39 | "eslint-config-standard": "^10.2.1", 40 | "eslint-plugin-import": "^2.7.0", 41 | "eslint-plugin-node": "^5.2.0", 42 | "eslint-plugin-promise": "^3.5.0", 43 | "eslint-plugin-standard": "^3.0.1", 44 | "json-schema-ref-parser": "^3.3.1", 45 | "mocha": "^4.0.1", 46 | "nyc": "^11.2.1", 47 | "require-uncached": "^1.0.3", 48 | "vue": "^2.5.1", 49 | "vuelidate": "^0.6.1" 50 | }, 51 | "dependencies": { 52 | "flat": "^4.0.0", 53 | "lodash": "^4.17.4" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var mergeStrategy = require('./merge-validation-options') 3 | var reduce = require('lodash/reduce') 4 | var merge = require('lodash/merge') 5 | var set = require('lodash/set') 6 | var get = require('lodash/get') 7 | var difference = require('lodash/difference') 8 | var defaultsDeep = require('lodash/defaultsDeep') 9 | var omit = require('lodash/omit') 10 | var isPlainObject = require('lodash/isPlainObject') 11 | var cloneDeep = require('lodash/cloneDeep') 12 | var isFunction = require('lodash/isFunction') 13 | var flattenObject = require('flat') 14 | var propertyRules = require('./property-rules') 15 | var createDataProperties = require('./scaffold') 16 | 17 | var cachedVue 18 | 19 | function getVue(rootVm) { 20 | if (cachedVue) { 21 | return cachedVue 22 | } 23 | var InnerVue = rootVm.constructor 24 | /* istanbul ignore next */ 25 | while (InnerVue.super) { 26 | InnerVue = InnerVue.super 27 | } 28 | cachedVue = InnerVue 29 | return InnerVue 30 | } 31 | 32 | function normalizeSchemas(schemaConfig) { 33 | if (Array.isArray(schemaConfig)) { 34 | return schemaConfig.map(function(config) { 35 | if (config.mountPoint) { 36 | return config 37 | } else { 38 | return {mountPoint: 'schema', schema: config} 39 | } 40 | }) 41 | } else { 42 | if (schemaConfig.mountPoint) { 43 | return [schemaConfig] 44 | } else { 45 | return [ 46 | { 47 | mountPoint: 'schema', 48 | schema: schemaConfig 49 | } 50 | ] 51 | } 52 | } 53 | } 54 | 55 | function createFromSchema(schema, values) { 56 | var generated = createDataProperties([ 57 | { 58 | mountPoint: '.', 59 | schema: schema 60 | } 61 | ]) 62 | return defaultsDeep(values, generated) 63 | } 64 | 65 | function generateValidationSchema(schemas) { 66 | var root = {} 67 | var self = this 68 | 69 | var roots = schemas.filter(function(schemaConfig) { 70 | return schemaConfig.mountPoint === '.' 71 | }) 72 | 73 | roots.forEach(function(schemaConfig) { 74 | if (schemaConfig.schema.type !== 'object') { 75 | throw new Error('Schema with id ' + schemaConfig.schema.id + ' has mount point at the root and is not a schema of type object. This is not supported. For non object schmeas you must define a mount point.') 76 | } 77 | 78 | if (schemaConfig.schema.hasOwnProperty('patternProperties')) { 79 | throw new Error('Schema with id ' + schemaConfig.schema.id + ' has sibling validator patternProperties. This is not supported when mounting on root. Use a mount point.') 80 | } 81 | 82 | if (!(schemaConfig.schema.additionalProperties === true || schemaConfig.schema.additionalProperties === undefined)) { 83 | throw new Error('Schema with id ' + schemaConfig.schema.id + ' has sibling validators additionalProperties not equal to true or undefined. This is not supported when mounting on root. Since there are lots of extra properties on a vue instance.') 84 | } 85 | }) 86 | 87 | if (roots.length) { 88 | root = roots.reduce(function(all, schemaConfig) { 89 | merge(all, propertyRules.getPropertyValidationRules.call(self, schemaConfig.schema, true, true)) 90 | return all 91 | }, root) 92 | } 93 | 94 | var rest = difference(schemas, roots) 95 | 96 | return reduce(rest, function(all, schemaConfig) { 97 | var existing = get(all, schemaConfig.mountPoint) 98 | var parents = schemaConfig.mountPoint.split('.') 99 | set(all, schemaConfig.mountPoint, merge(existing, propertyRules.getPropertyValidationRules.call(self, schemaConfig.schema, true, true, null, parents))) 100 | return all 101 | }, root) 102 | } 103 | 104 | function createMixin(options) { 105 | options = options || {} 106 | return { 107 | beforeCreate: function() { 108 | var self = this 109 | var schema = this.$options.schema 110 | var propsData = this.$options.propsData 111 | var normalized = [] 112 | 113 | if (schema) { 114 | normalized = normalizeSchemas(schema) 115 | } 116 | 117 | if (propsData && propsData[options.schemaPropsName]) { 118 | normalized = normalized.concat(normalizeSchemas(propsData[options.schemaPropsName])) 119 | } 120 | 121 | if (!normalized.length) { 122 | return 123 | } 124 | 125 | var Vue = getVue(this) 126 | 127 | this.$options.validations = mergeStrategy(function() { 128 | if (this.$schema && !isFunction(this.$schema.then)) { 129 | return generateValidationSchema.call(this, this.$schema) 130 | } else { 131 | return {} 132 | } 133 | }, this.$options.validations) 134 | 135 | this.$options.methods = Vue.config.optionMergeStrategies.methods({ 136 | createFromSchema: createFromSchema, 137 | getSchemaData: function(schemaConfig) { 138 | var originallyArray = Array.isArray(schemaConfig) 139 | var normalizedSchemas = Array.isArray(schemaConfig) 140 | ? schemaConfig 141 | : [schemaConfig] 142 | var self = this 143 | return reduce(normalizedSchemas, function(all, schema) { 144 | var root = self 145 | var data = createDataProperties(normalizedSchemas) 146 | var flatStructure = isPlainObject(data) 147 | ? flattenObject(data) 148 | : {} 149 | if (schemaConfig.mountPoint !== '.' && !originallyArray) { 150 | data = get(self, schemaConfig.mountPoint) 151 | flatStructure = isPlainObject(data) 152 | ? flattenObject(data) 153 | : {} 154 | root = get(self, schemaConfig.mountPoint) 155 | 156 | if (isPlainObject(root)) { 157 | flatStructure = reduce(flatStructure, function(all, val, key) { 158 | all[String(key).replace(schemaConfig.mountPoint, '').replace(/^\./, '')] = val 159 | return all 160 | }, {}) 161 | } else { 162 | return root 163 | } 164 | } 165 | 166 | return reduce(flatStructure, function(all, val, path) { 167 | return set(all, path, get(root, path)) 168 | }, all) 169 | }, {}) 170 | } 171 | }, this.$options.methods) 172 | 173 | var calledSchemas = normalized.map(function(schemaConfig) { 174 | if (isFunction(schemaConfig.schema)) { 175 | var config = cloneDeep(schemaConfig) 176 | config.schema = schemaConfig.schema.call(self) 177 | return config 178 | } 179 | 180 | return schemaConfig 181 | }) 182 | 183 | var hasPromise = calledSchemas.some(function(schemaConfig) { 184 | return isFunction(schemaConfig.schema.then) 185 | }) 186 | 187 | if (hasPromise) { 188 | calledSchemas.forEach(function(config, i) { 189 | if (config.mountPoint === '.' && isFunction(config.schema.then)) { 190 | throw new Error('Schema with index ' + i + ' has mount point at the root and is a promise. This is not supported. You can\'t mount to root async. Due to vue limitation. Use a mount point.') 191 | } 192 | }) 193 | 194 | var allSchemaPromise = Promise.all(calledSchemas.map(function(schemaConfig) { 195 | if (isFunction(schemaConfig.schema.then)) { 196 | return schemaConfig.schema.then(function(schema) { 197 | var newConfig = omit(schemaConfig, 'schema') 198 | newConfig.schema = schema 199 | return newConfig 200 | }) 201 | } else { 202 | return schemaConfig 203 | } 204 | })).then(function(schemaConfigs) { 205 | // reactivity is already set up, we can just replace properties 206 | Object.assign(self, createDataProperties(schemaConfigs)) 207 | self.$schema = schemaConfigs 208 | }) 209 | 210 | Vue.util.defineReactive(this, '$schema', allSchemaPromise) 211 | this.$options.data = Vue.config.optionMergeStrategies.data(function() { 212 | return createDataProperties(calledSchemas, true) 213 | }, this.$options.data) 214 | } else { 215 | // rewrite schemas normalized 216 | Vue.util.defineReactive(this, '$schema', calledSchemas) 217 | this.$options.data = Vue.config.optionMergeStrategies.data(function() { 218 | return createDataProperties(calledSchemas) 219 | }, this.$options.data) 220 | } 221 | } 222 | } 223 | } 224 | 225 | module.exports = { 226 | createFromSchema: createFromSchema, 227 | mixin: createMixin(), 228 | install: function(Vue, options) { 229 | Vue.mixin(createMixin(options)) 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/merge-validation-options.js: -------------------------------------------------------------------------------- 1 | var isFunction = require('lodash/isFunction') 2 | var mergeWith = require('lodash/mergeWith') 3 | var forEach = require('lodash/forEach') 4 | var isPlainObject = require('lodash/isPlainObject') 5 | 6 | function noopValidator() { 7 | return true 8 | } 9 | 10 | function customizer(objValue, srcValue, key, object, source, stack) { 11 | if (srcValue === undefined) { 12 | return noopValidator 13 | } 14 | } 15 | 16 | function deleteNoopValidators(obj) { 17 | forEach(obj, function(val, key) { 18 | if (val === noopValidator) { 19 | delete obj[key] 20 | } else if (isPlainObject(val)) { 21 | deleteNoopValidators(val) 22 | } 23 | }) 24 | } 25 | 26 | module.exports = function(toVal, fromVal) { 27 | if (!toVal) return fromVal 28 | if (!fromVal) return toVal 29 | var retVal 30 | if (isFunction(toVal) || isFunction(fromVal)) { 31 | retVal = function() { 32 | var args = [].slice.call(arguments) 33 | var toResult = isFunction(toVal) ? toVal.apply(this, args) : toVal 34 | var fromResult = isFunction(fromVal) ? fromVal.apply(this, args) : fromVal 35 | var merged = mergeWith(toResult, fromResult, customizer) 36 | 37 | deleteNoopValidators(merged) 38 | return merged 39 | } 40 | } else { 41 | retVal = mergeWith(toVal, fromVal, customizer) 42 | deleteNoopValidators(retVal) 43 | } 44 | 45 | return retVal 46 | } 47 | -------------------------------------------------------------------------------- /src/property-rules.js: -------------------------------------------------------------------------------- 1 | var additionalItemsValidator = require('./validators/additionalItems') 2 | var additionalPropertiesValidator = require('./validators/additionalProperties') 3 | var anyOfValidator = require('./validators/anyOf') 4 | var betweenValidator = require('./validators/between') 5 | var containsValidator = require('./validators/contains') 6 | var dependenciesValidator = require('./validators/dependencies') 7 | var enumValidator = require('./validators/enum') 8 | var equalValidator = require('./validators/const') 9 | var exclusiveMaxValidator = require('./validators/exclusiveMaximum') 10 | var exclusiveMinValidator = require('./validators/exclusiveMinimum') 11 | var get = require('lodash/get') 12 | var isFunction = require('lodash/isFunction') 13 | var isPlainObject = require('lodash/isPlainObject') 14 | var itemsValidator = require('./validators/items') 15 | var maxItemsValidator = require('./validators/maxItems') 16 | var maxLengthValidator = require('./validators/maxLength') 17 | var maxPropertiesValidator = require('./validators/maxProperties') 18 | var maxValidator = require('./validators/maximum') 19 | var minItemsValidator = require('./validators/minItems') 20 | var minLengthValidator = require('./validators/minLength') 21 | var minPropertiesValidator = require('./validators/minProperties') 22 | var minValidator = require('./validators/minimum') 23 | var multipleOfValidator = require('./validators/multipleOf') 24 | var notValidator = require('./validators/not') 25 | var oneOfValidator = require('./validators/oneOf') 26 | var patternPropertiesValidator = require('./validators/patternProperties') 27 | var patternValidator = require('./validators/pattern') 28 | var propertyNamesValidator = require('./validators/propertyNames') 29 | var reduce = require('lodash/reduce') 30 | var requiredValidator = require('./validators/required') 31 | var typeArrayValidator = require('./validators/typeArray') 32 | var typeValidator = require('./validators/type') 33 | var uniq = require('lodash/uniq') 34 | var uniqueValidator = require('./validators/uniqueItems') 35 | var validators = require('vuelidate/lib/validators') 36 | 37 | function mergeIntoArray(to, from) { 38 | var allKeys = uniq(Object.keys(to).concat(Object.keys(from))) 39 | 40 | allKeys.forEach(function(key) { 41 | var toVal = to[key] 42 | var fromVal = from[key] 43 | 44 | if (to.hasOwnProperty(key) && from.hasOwnProperty(key) && isFunction(fromVal)) { 45 | to[key] = [].concat(toVal).concat(fromVal) 46 | } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { 47 | mergeIntoArray(toVal, fromVal) 48 | } else if (fromVal) { 49 | to[key] = fromVal 50 | } 51 | }) 52 | } 53 | 54 | function createAndValidator(obj) { 55 | Object.keys(obj).forEach(function(key) { 56 | // TODO: are array valid in a vuelidate validations config? if so we need a different approach 57 | var value = obj[key] 58 | if (Array.isArray(value)) { 59 | obj[key] = validators.and.apply(null, value) 60 | } else if (isPlainObject(value)) { 61 | createAndValidator(value) 62 | } 63 | }) 64 | } 65 | 66 | function mostBeUndefined(val) { 67 | return val === undefined 68 | } 69 | 70 | function getPropertyValidationRules(propertySchema, isRequired, isAttached, propKey, parents) { 71 | var validationObj = {} 72 | var self = this 73 | parents = parents || [] 74 | 75 | // support for boolean schemas 76 | if (propertySchema === true) { 77 | return validationObj 78 | } else if (propertySchema === false) { 79 | validationObj.schemaNotPresent = mostBeUndefined 80 | return validationObj 81 | } 82 | 83 | function has(name) { 84 | return propertySchema.hasOwnProperty(name) 85 | } 86 | 87 | // add child properties 88 | if (has('properties')) { 89 | var req = propertySchema.required || [] 90 | validationObj = reduce(propertySchema.properties, function(all, childPropSchema, propKey) { 91 | var propRequired = req.indexOf(propKey) !== -1 92 | all[propKey] = getPropertyValidationRules.call(self, childPropSchema, propRequired, isAttached, propKey, parents.concat(propKey)) 93 | return all 94 | }, validationObj) 95 | } 96 | 97 | if (Array.isArray(propertySchema.type)) { 98 | validationObj.schemaTypes = typeArrayValidator(propertySchema, propertySchema.type.map(function(type) { 99 | return typeValidator(propertySchema, type) 100 | })) 101 | } else if (has('type')) { 102 | validationObj.schemaType = typeValidator(propertySchema, propertySchema.type) 103 | } 104 | 105 | if (isRequired) { 106 | validationObj.schemaRequired = requiredValidator(propertySchema, isAttached) 107 | } 108 | 109 | if (has('oneOf')) { 110 | validationObj.schemaOneOf = oneOfValidator(propertySchema, propertySchema.oneOf, getPropertyValidationRules) 111 | } 112 | 113 | if (has('anyOf')) { 114 | validationObj.schemaAnyOf = anyOfValidator(propertySchema, propertySchema.anyOf, getPropertyValidationRules) 115 | } 116 | 117 | if (has('not')) { 118 | validationObj.schemaNot = notValidator(propertySchema, propertySchema.not, getPropertyValidationRules) 119 | } 120 | 121 | if (has('additionalItems')) { 122 | validationObj.schemaAdditionalItems = additionalItemsValidator(propertySchema, propertySchema.additionalItems, getPropertyValidationRules) 123 | } 124 | 125 | if (has('contains')) { 126 | validationObj.schemaContains = containsValidator(propertySchema, propertySchema.contains, getPropertyValidationRules) 127 | } 128 | 129 | if (has('dependencies')) { 130 | validationObj.schemaDependencies = dependenciesValidator(propertySchema, propertySchema.dependencies, getPropertyValidationRules) 131 | } 132 | 133 | if (has('minLength')) { 134 | validationObj.schemaMinLength = minLengthValidator(propertySchema, propertySchema.minLength) 135 | } 136 | 137 | if (has('maxLength')) { 138 | validationObj.schemaMaxLength = maxLengthValidator(propertySchema, propertySchema.maxLength) 139 | } 140 | 141 | if (has('minItems')) { 142 | validationObj.schemaMinItems = minItemsValidator(propertySchema, propertySchema.minItems) 143 | } 144 | 145 | if (has('maxItems')) { 146 | validationObj.schemaMaxItems = maxItemsValidator(propertySchema, propertySchema.maxItems) 147 | } 148 | 149 | if (has('minimum') && has('maximum')) { 150 | validationObj.schemaBetween = betweenValidator(propertySchema, propertySchema.minimum, propertySchema.maximum) 151 | } else if (has('minimum')) { 152 | validationObj.schemaMinimum = minValidator(propertySchema, propertySchema.minimum) 153 | } else if (has('maximum')) { 154 | validationObj.schemaMaximum = maxValidator(propertySchema, propertySchema.maximum) 155 | } 156 | 157 | if (has('exclusiveMinimum')) { 158 | validationObj.schemaExclusiveMinimum = exclusiveMinValidator(propertySchema, propertySchema.exclusiveMinimum) 159 | } 160 | 161 | if (has('exclusiveMaximum')) { 162 | validationObj.schemaExclusiveMaximum = exclusiveMaxValidator(propertySchema, propertySchema.exclusiveMaximum) 163 | } 164 | 165 | if (has('maxProperties')) { 166 | validationObj.schemaMaxProperties = maxPropertiesValidator(propertySchema, propertySchema.maxProperties) 167 | } 168 | 169 | if (has('minProperties')) { 170 | validationObj.schemaMinProperties = minPropertiesValidator(propertySchema, propertySchema.minProperties) 171 | } 172 | 173 | if (has('multipleOf')) { 174 | validationObj.schemaMultipleOf = multipleOfValidator(propertySchema, propertySchema.multipleOf) 175 | } 176 | 177 | if (has('pattern')) { 178 | validationObj.schemaPattern = patternValidator(propertySchema, new RegExp(propertySchema.pattern)) 179 | } 180 | 181 | if (has('patternProperties')) { 182 | validationObj.schemaPatternProperties = patternPropertiesValidator(propertySchema, propertySchema.patternProperties, getPropertyValidationRules) 183 | } 184 | 185 | if (has('propertyNames')) { 186 | validationObj.schemaPropertyNames = propertyNamesValidator(propertySchema, propertySchema.propertyNames, getPropertyValidationRules) 187 | } 188 | 189 | if (has('additionalProperties')) { 190 | validationObj.schemaAdditionalProperties = additionalPropertiesValidator(propertySchema, propertySchema.additionalProperties, getPropertyValidationRules) 191 | } 192 | 193 | if (has('enum')) { 194 | validationObj.schemaEnum = enumValidator(propertySchema, propertySchema.enum) 195 | } 196 | 197 | if (has('const')) { 198 | validationObj.schemaConst = equalValidator(propertySchema, propertySchema.const) 199 | } 200 | 201 | if (has('uniqueItems')) { 202 | validationObj.schemaUniqueItems = uniqueValidator(propertySchema) 203 | } 204 | 205 | // if we have a singular type of array then we don't need the dynamic regeneration 206 | if (has('items') && propertySchema.type === 'array' && isPlainObject(propertySchema.items)) { 207 | validationObj.$each = getPropertyValidationRules(propertySchema.items, true, true, null, parents.concat(0)) 208 | } else if (has('items') && isPlainObject(propertySchema.items)) { 209 | // A bit costly maybe but regenerate validations if the property is not an array anymore 210 | if (Array.isArray(get(this, parents.join('.')))) { 211 | validationObj.$each = getPropertyValidationRules(propertySchema.items, true, true, null, parents.concat(0)) 212 | } 213 | } else if (has('items')) { 214 | validationObj.schemaItems = itemsValidator(propertySchema, getPropertyValidationRules) 215 | } 216 | 217 | if (has('allOf')) { 218 | propertySchema.allOf.forEach(function(schema) { 219 | mergeIntoArray(validationObj, getPropertyValidationRules(schema, false, isAttached)) 220 | }) 221 | 222 | createAndValidator(validationObj) 223 | } 224 | 225 | return validationObj 226 | } 227 | 228 | module.exports = { 229 | getPropertyValidationRules: getPropertyValidationRules 230 | } 231 | -------------------------------------------------------------------------------- /src/scaffold.js: -------------------------------------------------------------------------------- 1 | var reduce = require('lodash/reduce') 2 | var merge = require('lodash/merge') 3 | var set = require('lodash/set') 4 | var get = require('lodash/get') 5 | var isPlainObject = require('lodash/isPlainObject') 6 | var isFunction = require('lodash/isFunction') 7 | 8 | var defaultValues = { 9 | integer: 0, 10 | number: 0, 11 | string: '', 12 | boolean: false, 13 | object: function() { 14 | return {} // make sure we don't share object reference, create new copy each time 15 | }, 16 | array: function() { 17 | return [] // make sure we don't share object reference, create new copy each time 18 | }, 19 | 'null': null 20 | } 21 | 22 | function getDefaultValue(schema, isRequired, ignoreDefaultProp) { 23 | if (ignoreDefaultProp) { 24 | return undefined 25 | } else if (schema.hasOwnProperty('default')) { 26 | return schema.default 27 | } else if (schema.hasOwnProperty('const')) { 28 | return schema.const 29 | } else if (!isRequired) { 30 | return undefined 31 | } else { 32 | var defaultValue = defaultValues[schema.type] 33 | return isFunction(defaultValue) 34 | ? defaultValue() 35 | : defaultValue 36 | } 37 | } 38 | 39 | function setProperties(base, schema, ignoreDefaultProp, shallow) { 40 | if (schema.default !== undefined) { 41 | return Object.assign(base, schema.default) 42 | } 43 | if (!base) return 44 | var additionalScaffoldingSchemas = ['allOf'] 45 | var additionalShallow = ['anyOf', 'oneOf', 'not'] 46 | // set all properties based on default values etc in allOf 47 | 48 | additionalScaffoldingSchemas.forEach(function(prop) { 49 | if (Array.isArray(schema[prop])) { 50 | schema[prop].forEach(function(subSchema) { 51 | setProperties(base, subSchema) 52 | }) 53 | } 54 | }) 55 | 56 | additionalShallow.forEach(function(prop) { 57 | if (Array.isArray(schema[prop])) { 58 | schema[prop].forEach(function(subSchema) { 59 | setProperties(base, subSchema, true, true) 60 | }) 61 | } else if (isPlainObject(schema[prop])) { 62 | setProperties(base, schema[prop], true, true) 63 | } 64 | }) 65 | 66 | // then add properties from base object, taking precedence 67 | if (isPlainObject(schema.properties)) { 68 | Object.keys(schema.properties).forEach(function(key) { 69 | var innerSchema = schema.properties[key] 70 | var isRequired = Array.isArray(schema.required) && schema.required.indexOf(key) !== -1 71 | 72 | var existing = base[key] 73 | if (isPlainObject(existing)) { 74 | base[key] = merge(existing, getDefaultValue(innerSchema, isRequired, ignoreDefaultProp)) 75 | } else { 76 | base[key] = getDefaultValue(innerSchema, isRequired, ignoreDefaultProp) 77 | } 78 | if (!shallow && innerSchema.type === 'object' && innerSchema.properties) { 79 | setProperties(base[key], innerSchema, ignoreDefaultProp) 80 | } 81 | }) 82 | } 83 | } 84 | 85 | function createDataProperties(schemas, shallow) { 86 | var allAreArray = schemas.every(function (schema) { 87 | return schema.type === 'array' 88 | }) 89 | 90 | return reduce(schemas, function(all, schemaConfig) { 91 | if (shallow) { 92 | set(all, schemaConfig.mountPoint, undefined) 93 | return all 94 | } 95 | 96 | if (schemaConfig.mountPoint !== '.') { 97 | // scaffold structure 98 | 99 | var mountPoint = get(all, schemaConfig.mountPoint) 100 | var defValue = schemaConfig.schema.hasOwnProperty('type') ? getDefaultValue(schemaConfig.schema, true) : {} 101 | 102 | if (isPlainObject(mountPoint)) { 103 | mountPoint = merge(mountPoint, defValue) 104 | } else { 105 | mountPoint = defValue 106 | } 107 | 108 | set(all, schemaConfig.mountPoint, mountPoint) 109 | if (isPlainObject(mountPoint)) { 110 | setProperties(get(all, schemaConfig.mountPoint), schemaConfig.schema) 111 | } 112 | } else { 113 | setProperties(all, schemaConfig.schema) 114 | } 115 | 116 | return all 117 | }, allAreArray ? [] : {}) 118 | } 119 | 120 | module.exports = createDataProperties 121 | -------------------------------------------------------------------------------- /src/validate.js: -------------------------------------------------------------------------------- 1 | var isPlainObject = require('lodash/isPlainObject') 2 | var every = require('lodash/every') 3 | 4 | function validateGroup(item, validator, key) { 5 | if (isPlainObject(validator)) { 6 | return every(validator, function(innerValidator, innerKey) { 7 | if (item === undefined || item === null) { 8 | return true 9 | } 10 | 11 | if (innerKey === '$each') { 12 | if (!Array.isArray(item[key])) return true // TODO is this correct when not array? 13 | return item[key].every(function (value) { 14 | return every(innerValidator, function(validator, index) { 15 | return validateGroup(value, validator, index) 16 | }) 17 | }) 18 | } 19 | 20 | return validateGroup(item[key], innerValidator, innerKey) 21 | }) 22 | } else { 23 | return validator(item) 24 | } 25 | } 26 | 27 | module.exports = function(validators, value) { 28 | return every(validators, function(validator, key) { 29 | return validateGroup(value, validator, key) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/validators/additionalItems.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var validate = require('../validate') 3 | 4 | module.exports = function additionalItemsValidator(arraySchema, additionalItems, getPropertyValidationRules) { 5 | return vuelidate.withParams({ 6 | type: 'schemaAdditionalItems', 7 | schema: arraySchema 8 | }, function(values) { 9 | if (!Array.isArray(values) || values.length === 0 || !Array.isArray(arraySchema.items)) { 10 | return true 11 | } 12 | 13 | var extraItems = values.slice(arraySchema.items.length) 14 | 15 | if (!extraItems.length) return true 16 | if (additionalItems === false) return false 17 | 18 | var rules = getPropertyValidationRules(additionalItems) 19 | 20 | return extraItems.every(function(value) { 21 | return validate(rules, value) 22 | }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /src/validators/additionalProperties.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | var every = require('lodash/every') 4 | var pullAll = require('lodash/pullAll') 5 | var validate = require('../validate') 6 | 7 | module.exports = function additionalPropertiesValidator(propertySchema, additionalProperties, getPropertyValidationRules) { 8 | return vuelidate.withParams({ 9 | type: 'schemaAdditionalProperties', 10 | additionalProperties: additionalProperties, 11 | schema: propertySchema 12 | }, function(object) { 13 | if (!object || !isPlainObject(object)) return true 14 | var keys = Object.keys(object) 15 | var properties = Object.keys(propertySchema.properties || {}) 16 | var additionalKeys 17 | if (additionalProperties === true || additionalProperties === undefined) { 18 | return true 19 | } else { 20 | if (!propertySchema.patternProperties) { 21 | if (additionalProperties === false) { 22 | return keys.every(function(key) { 23 | return properties.indexOf(key) !== -1 24 | }) 25 | } else { 26 | additionalKeys = pullAll(keys, properties) 27 | } 28 | } else { 29 | var patternKeys = Object.keys(propertySchema.patternProperties).map(function(key) { 30 | return new RegExp(key) 31 | }) 32 | 33 | additionalKeys = keys.filter(function(key) { 34 | if (propertySchema.properties && propertySchema.properties[key]) { return false } 35 | 36 | return !patternKeys.some(function(regexp) { 37 | return regexp.test(key) 38 | }) 39 | }) 40 | } 41 | 42 | if (additionalProperties === false) { 43 | return additionalKeys.length === 0 44 | } else if (isPlainObject(additionalProperties)) { 45 | var validatorGroup = getPropertyValidationRules(additionalProperties) 46 | 47 | return every(additionalKeys, function(additionalKey) { 48 | var innerProp = object[additionalKey] 49 | return validate(validatorGroup, innerProp) 50 | }) 51 | } 52 | } 53 | 54 | return true 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /src/validators/allOf.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var typeValidator = require('./type') 4 | var validate = require('../validate') 5 | // this validator is currently not in use 6 | module.exports = function allOfValidator(propertySchema, schemas, getPropertyValidationRules) { 7 | return vuelidate.withParams({ 8 | type: 'schemaAllOf', 9 | schemas: schemas, 10 | schema: propertySchema 11 | }, function(val) { 12 | if (!noParamsRequired(val)) { 13 | return true 14 | } 15 | 16 | // ignore type errors, the type validator handles that 17 | if (!typeValidator(propertySchema, propertySchema.type)(val)) { 18 | return true 19 | } 20 | 21 | return schemas.every(function(itemSchema) { 22 | return validate(getPropertyValidationRules(itemSchema), val) 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/validators/anyOf.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var typeValidator = require('./type') 4 | var validate = require('../validate') 5 | 6 | module.exports = function anyOfValidator(propertySchema, schemas, getPropertyValidationRules) { 7 | return vuelidate.withParams({ 8 | type: 'schemaAnyOf', 9 | schemas: schemas, 10 | schema: propertySchema 11 | }, function(val) { 12 | if (!noParamsRequired(val)) { 13 | return true 14 | } 15 | 16 | // ignore type errors, the type validator handles that 17 | if (!typeValidator(propertySchema, propertySchema.type)(val)) { 18 | return true 19 | } 20 | 21 | return schemas.some(function(itemSchema) { 22 | return validate(getPropertyValidationRules(itemSchema), val) 23 | }) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/validators/between.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | 4 | module.exports = function betweenValidator(propertySchema, min, max) { 5 | return vuelidate.withParams({ 6 | type: 'schemaBetween', 7 | min: min, 8 | max: max, 9 | schema: propertySchema 10 | }, function(val) { 11 | if (!isFinite(val)) return true 12 | return val >= min && val <= max 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /src/validators/const.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isEqual = require('lodash/isEqual') 3 | var noParamsRequired = require('./noParamsRequired') 4 | 5 | module.exports = function equalValidator(propertySchema, equal) { 6 | return vuelidate.withParams({ 7 | type: 'schemaConst', 8 | equal: equal, 9 | schema: propertySchema 10 | }, function(val) { 11 | return !noParamsRequired(val) || isEqual(equal, val) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/contains.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var validate = require('../validate') 3 | 4 | module.exports = function containsValidator(propertySchema, contains, getPropertyValidationRules) { 5 | return vuelidate.withParams({ 6 | type: 'schemaContains', 7 | contains: contains, 8 | schema: propertySchema 9 | }, function(values) { 10 | if (!Array.isArray(values)) return true 11 | 12 | var validatorGroup = getPropertyValidationRules(contains) 13 | 14 | return values.some(function(value) { 15 | return validate(validatorGroup, value) 16 | }) 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /src/validators/dependencies.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | var validate = require('../validate') 4 | 5 | module.exports = function dependenciesValidator(propertySchema, dependencies, getPropertyValidationRules) { 6 | return vuelidate.withParams({ 7 | type: 'schemaDependencies', 8 | dependencies: dependencies, 9 | schema: propertySchema 10 | }, function(obj) { 11 | if (!isPlainObject(obj)) { return true } 12 | 13 | var properties = Object.keys(obj) 14 | 15 | var propertiesCausingDependencyCheck = properties.filter(function(key) { 16 | return dependencies.hasOwnProperty(key) 17 | }) 18 | 19 | return propertiesCausingDependencyCheck.reduce(function(valid, key) { 20 | if (!valid) { return valid } 21 | 22 | var dependencyForProp = dependencies[key] 23 | if (Array.isArray(dependencyForProp)) { 24 | return dependencyForProp.every(function(dep) { 25 | return properties.indexOf(dep) !== -1 26 | }) 27 | } else { 28 | return validate(getPropertyValidationRules(dependencyForProp), obj) 29 | } 30 | }, true) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /src/validators/enum.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var isEqual = require('lodash/isEqual') 4 | 5 | module.exports = function oneOfValidator(propertySchema, choices) { 6 | return vuelidate.withParams({ 7 | type: 'schemaEnum', 8 | choices: choices, 9 | schema: propertySchema 10 | }, function(val) { 11 | if (!noParamsRequired(val)) return true 12 | return choices.some(function(choice) { 13 | return isEqual(val, choice) 14 | }) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/validators/exclusiveMaximum.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | 4 | module.exports = function exclusiveMaxValidator(propertySchema, max) { 5 | return vuelidate.withParams({ 6 | type: 'schemaExclusiveMaximum', 7 | max: max, 8 | schema: propertySchema 9 | }, function(val) { 10 | if (!isFinite(val)) return true 11 | return val < max 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/exclusiveMinimum.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | 4 | module.exports = function exclusiveMinimumValidator(propertySchema, min) { 5 | return vuelidate.withParams({ 6 | type: 'schemaExclusiveMinimum', 7 | min: min, 8 | schema: propertySchema 9 | }, function(val) { 10 | if (!isFinite(val)) return true 11 | return val > min 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/items.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var typeValidator = require('./type') 3 | var validate = require('../validate') 4 | 5 | module.exports = function itemsValidator(arraySchema, getPropertyValidationRules) { 6 | return vuelidate.withParams({ 7 | type: 'schemaItems', 8 | schema: arraySchema 9 | }, function(values) { 10 | if (!Array.isArray(values) || values.length === 0) { 11 | return true 12 | } 13 | 14 | // ignore type errors, the type validator handles that 15 | if (arraySchema.items.type === 'object') { 16 | var hasTypeError = values.some(function(val) { 17 | return !typeValidator(arraySchema, arraySchema.type)(val) 18 | }) 19 | 20 | if (hasTypeError) { 21 | return true 22 | } 23 | } 24 | 25 | var validators 26 | if (!Array.isArray(arraySchema.items)) { 27 | // use first schema always if originally one schema object 28 | // must do it this way because of normalization to an array 29 | validators = getPropertyValidationRules(arraySchema.items) 30 | return values.every(function(value) { 31 | return validate(validators, value) 32 | }) 33 | } else { 34 | validators = arraySchema.items.map(function(itemSchema) { 35 | return getPropertyValidationRules(itemSchema) 36 | }) 37 | 38 | return values.every(function(value, i) { 39 | return validate(validators[i], value) 40 | }) 41 | } 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /src/validators/maxItems.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | 3 | module.exports = function maxItemsValidator(propertySchema, max) { 4 | return vuelidate.withParams({ 5 | type: 'schemaMaxItems', 6 | schema: propertySchema, 7 | max: max 8 | }, function(val) { 9 | if (!Array.isArray(val)) return true 10 | return val.length <= max 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/validators/maxLength.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isString = require('lodash/isString') 3 | 4 | module.exports = function maxLengthValidator(propertySchema, max) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMaxLength', 7 | schema: propertySchema, 8 | max: max 9 | }, function(val) { 10 | if (!isString(val)) return true 11 | return val.length <= max 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/maxProperties.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | 4 | module.exports = function maxPropertiesValidator(propertySchema, max) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMaxProperties', 7 | max: max, 8 | schema: propertySchema 9 | }, function(object) { 10 | if (!isPlainObject(object)) return true 11 | return Object.keys(object).length <= max 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/maximum.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | 4 | module.exports = function maxValidator(propertySchema, max) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMaximum', 7 | max: max, 8 | schema: propertySchema 9 | }, function(val) { 10 | if (!isFinite(val)) return true 11 | return val <= max 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/minItems.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | 3 | module.exports = function minItemsValidator(propertySchema, min) { 4 | return vuelidate.withParams({ 5 | type: 'schemaMinItems', 6 | schema: propertySchema, 7 | min: min 8 | }, function(val) { 9 | if (!Array.isArray(val)) return true 10 | return val.length >= min 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/validators/minLength.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isString = require('lodash/isString') 3 | 4 | module.exports = function minLengthValidator(propertySchema, min) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMinLength', 7 | schema: propertySchema, 8 | min: min 9 | }, function(val) { 10 | if (!isString(val)) return true 11 | return val.length >= min 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/minProperties.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | 4 | module.exports = function minPropertiesValidator(propertySchema, min) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMinProperties', 7 | min: min, 8 | schema: propertySchema 9 | }, function(object) { 10 | if (!isPlainObject(object)) return true 11 | return Object.keys(object).length >= min 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/minimum.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | 4 | module.exports = function minValidator(propertySchema, min) { 5 | return vuelidate.withParams({ 6 | type: 'schemaMinimum', 7 | min: min, 8 | schema: propertySchema 9 | }, function(val) { 10 | if (!isFinite(val)) return true 11 | return val >= min 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/multipleOf.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isFinite = require('lodash/isFinite') 3 | var isInteger = require('lodash/isInteger') 4 | 5 | module.exports = function multipleOfValidator(propertySchema, divider) { 6 | return vuelidate.withParams({ 7 | type: 'schemaMultipleOf', 8 | divider: divider, 9 | schema: propertySchema 10 | }, function(val) { 11 | if (!isFinite(val)) return true 12 | return isInteger(val / divider) 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /src/validators/noParamsRequired.js: -------------------------------------------------------------------------------- 1 | module.exports = function noParamsRequired(val) { 2 | return val !== undefined 3 | } 4 | -------------------------------------------------------------------------------- /src/validators/not.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var typeValidator = require('./type') 4 | var validate = require('../validate') 5 | 6 | module.exports = function notValidator(propertySchema, notSchema, getPropertyValidationRules) { 7 | return vuelidate.withParams({ 8 | type: 'schemaNot', 9 | not: notSchema, 10 | schema: propertySchema 11 | }, function(val) { 12 | if (!noParamsRequired(val)) { 13 | return true 14 | } 15 | 16 | // ignore type errors, the type validator handles that 17 | if (!typeValidator(propertySchema, propertySchema.type)(val)) { 18 | return true 19 | } 20 | 21 | return !validate(getPropertyValidationRules(notSchema), val) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /src/validators/oneOf.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var typeValidator = require('./type') 4 | var validate = require('../validate') 5 | 6 | module.exports = function oneOfValidator(propertySchema, schemas, getPropertyValidationRules) { 7 | return vuelidate.withParams({ 8 | type: 'schemaOneOf', 9 | schemas: schemas, 10 | schema: propertySchema 11 | }, function(val) { 12 | if (!noParamsRequired(val)) { 13 | return true 14 | } 15 | 16 | // ignore type errors, the type validator handles that 17 | if (!typeValidator(propertySchema, propertySchema.type)(val)) { 18 | return true 19 | } 20 | 21 | return schemas.reduce(function(matching, schema) { 22 | if (matching > 1) return 2 23 | if (validate(getPropertyValidationRules(schema), val)) return matching + 1 24 | else return matching 25 | }, 0) === 1 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /src/validators/pattern.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isString = require('lodash/isString') 3 | 4 | module.exports = function patternValidator(propertySchema, pattern) { 5 | return vuelidate.withParams({ 6 | type: 'schemaPattern', 7 | pattern: pattern, 8 | schema: propertySchema 9 | }, function(val) { 10 | if (!isString(val)) return true 11 | return pattern.test(val) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/validators/patternProperties.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | var every = require('lodash/every') 4 | var reduce = require('lodash/reduce') 5 | var filter = require('lodash/filter') 6 | var validate = require('../validate') 7 | 8 | module.exports = function patternPropertiesValidator(propertySchema, patternProperties, getPropertyValidationRules) { 9 | return vuelidate.withParams({ 10 | type: 'schemaPatternProperties', 11 | patternProperties: patternProperties, 12 | schema: propertySchema 13 | }, function(object) { 14 | if (!isPlainObject(object)) return true 15 | 16 | if (propertySchema.additionalProperties !== undefined && 17 | !isPlainObject(propertySchema.additionalProperties) && 18 | propertySchema.additionalProperties !== true 19 | ) { 20 | var allowedKeys = Object.keys(patternProperties).map(function(key) { 21 | return new RegExp(key) 22 | }) 23 | 24 | var allKeysAreValid = Object.keys(object).every(function(key) { 25 | // we have key, therefore it is valid 26 | if (propertySchema.properties && propertySchema.properties[key]) return true 27 | 28 | return allowedKeys.some(function(regexp) { 29 | return regexp.test(key) 30 | }) 31 | }) 32 | 33 | // fail early if some keys are not present 34 | if (!allKeysAreValid) { 35 | return false 36 | } 37 | } 38 | 39 | var schemasForKeys = reduce(object, function(all, value, key) { 40 | all[key] = filter(patternProperties, function(schema, pattern) { 41 | return new RegExp(pattern).test(key) 42 | }) 43 | return all 44 | }, {}) 45 | 46 | return every(schemasForKeys, function(schemas, key) { 47 | return schemas.every(function(itemSchema) { 48 | return validate(getPropertyValidationRules(itemSchema), object[key]) 49 | }) 50 | }) 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/validators/propertyNames.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var isPlainObject = require('lodash/isPlainObject') 3 | var validate = require('../validate') 4 | 5 | module.exports = function propertyNamesValidator(propertySchema, propertyNames, getPropertyValidationRules) { 6 | return vuelidate.withParams({ 7 | type: 'schemaPropertyNames', 8 | propertyNames: propertyNames, 9 | schema: propertySchema 10 | }, function(obj) { 11 | if (!isPlainObject(obj)) return true 12 | var properties = Object.keys(obj) 13 | var validatorGroup = getPropertyValidationRules(propertyNames) 14 | 15 | return properties.every(function(property) { 16 | return validate(validatorGroup, property) 17 | }) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/validators/required.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var isPlainObject = require('lodash/isPlainObject') 4 | 5 | module.exports = function requiredValidator(propertySchema, isAttached) { 6 | return vuelidate.withParams({ 7 | type: 'schemaRequired', 8 | schema: propertySchema 9 | }, function(val, parent) { 10 | if (!isPlainObject(parent) && isAttached) { 11 | return true 12 | } else { 13 | return noParamsRequired(val) 14 | } 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /src/validators/type.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var noParamsRequired = require('./noParamsRequired') 3 | var isNull = require('lodash/isNull') 4 | var isInteger = require('lodash/isInteger') 5 | var isFinite = require('lodash/isFinite') 6 | var isBoolean = require('lodash/isBoolean') 7 | var isString = require('lodash/isString') 8 | var isObject = require('lodash/isObject') 9 | 10 | var jsonTypes = { 11 | string: isString, 12 | object: function(val) { 13 | return isObject(val) && !Array.isArray(val) 14 | }, 15 | boolean: isBoolean, 16 | array: Array.isArray, 17 | 'null': isNull, 18 | number: isFinite, 19 | integer: isInteger 20 | } 21 | 22 | module.exports = function typeValidator(propertySchema, type) { 23 | return vuelidate.withParams({ 24 | type: 'schemaType', 25 | jsonType: type, 26 | schema: propertySchema 27 | }, function(val) { 28 | if (!type) return true 29 | return !noParamsRequired(val) || jsonTypes[type](val) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/validators/typeArray.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var validators = require('vuelidate/lib/validators') 3 | 4 | module.exports = function typeArrayValidator(propertySchema, validationCollection) { 5 | return vuelidate.withParams({ 6 | type: 'schemaTypes', 7 | schema: propertySchema 8 | }, function(val) { 9 | return validators.or.apply(validators, validationCollection)(val) 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /src/validators/uniqueItems.js: -------------------------------------------------------------------------------- 1 | var vuelidate = require('vuelidate') 2 | var uniqBy = require('lodash/uniqBy') 3 | var isPlainObject = require('lodash/isPlainObject') 4 | 5 | function getUniqueness(item) { 6 | if (isPlainObject(item) || Array.isArray(item)) { 7 | return JSON.stringify(item) 8 | } else { 9 | return item 10 | } 11 | } 12 | 13 | module.exports = function uniqueValidator(propertySchema) { 14 | return vuelidate.withParams({ 15 | type: 'schemaUniqueItems', 16 | schema: propertySchema 17 | }, function(val) { 18 | if (!Array.isArray(val)) { 19 | return true 20 | } 21 | if (val.length < 2) { 22 | return true 23 | } 24 | return val.length === uniqBy(val, getUniqueness).length 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "describe": true, 4 | "it": true, 5 | "beforeEach": true, 6 | "afterEach": true, 7 | "before": true, 8 | "after": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/schemas/additional.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "basic schema testing additional properties", 3 | "schema": { 4 | "$schema": "http://json-schema.org/draft-04/schema#", 5 | "type": "array", 6 | "items": { 7 | "title": "Product", 8 | "type": "object", 9 | "additionalProperties": false, 10 | "properties": { 11 | "id": { 12 | "type": "number" 13 | } 14 | }, 15 | "required": ["id"] 16 | } 17 | }, 18 | "tests": [{ 19 | "description": "valid array", 20 | "data": [{ 21 | "id": 2 22 | }, { 23 | "id": 3 24 | }], 25 | "valid": true 26 | }, { 27 | "description": "not array", 28 | "data": 1, 29 | "valid": false 30 | }, { 31 | "description": "array of not onjects", 32 | "data": [1, 2, 3], 33 | "valid": false 34 | }, { 35 | "description": "missing required properties", 36 | "data": [{}], 37 | "valid": false 38 | }, { 39 | "description": "additional property added", 40 | "data": [{ 41 | "id": 1, 42 | "name": null 43 | }], 44 | "valid": false 45 | }] 46 | }, { 47 | "description": "basic schema testing additional properties as schema", 48 | "schema": { 49 | "$schema": "http://json-schema.org/draft-04/schema#", 50 | "type": "array", 51 | "items": { 52 | "title": "Product", 53 | "type": "object", 54 | "additionalProperties": { 55 | "type": "boolean", 56 | "const": true 57 | }, 58 | "properties": { 59 | "id": { 60 | "type": "number" 61 | } 62 | }, 63 | "required": ["id"] 64 | } 65 | }, 66 | "tests": [{ 67 | "description": "valid array", 68 | "data": [{ 69 | "id": 2 70 | }, { 71 | "id": 3 72 | }], 73 | "valid": true 74 | }, { 75 | "description": "additional property added, not boolean", 76 | "data": [{ 77 | "id": 1, 78 | "name": null 79 | }], 80 | "valid": false 81 | }, { 82 | "description": "additional property added, boolean not true", 83 | "data": [{ 84 | "id": 1, 85 | "name": false 86 | }], 87 | "valid": false 88 | }, { 89 | "description": "additional property added, boolean VALID", 90 | "data": [{ 91 | "id": 1, 92 | "name": true 93 | }], 94 | "valid": true 95 | }] 96 | }] 97 | -------------------------------------------------------------------------------- /test/fixtures/schemas/additionalItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "additionalItems as schema", 4 | "schema": { 5 | "items": [{}], 6 | "additionalItems": {"type": "integer"} 7 | }, 8 | "tests": [ 9 | { 10 | "description": "additional items match schema", 11 | "data": [ null, 2, 3, 4 ], 12 | "valid": true 13 | }, 14 | { 15 | "description": "additional items do not match schema", 16 | "data": [ null, 2, 3, "foo" ], 17 | "valid": false 18 | } 19 | ] 20 | }, 21 | { 22 | "description": "items is schema, no additionalItems", 23 | "schema": { 24 | "items": {}, 25 | "additionalItems": false 26 | }, 27 | "tests": [ 28 | { 29 | "description": "all items match schema", 30 | "data": [ 1, 2, 3, 4, 5 ], 31 | "valid": true 32 | } 33 | ] 34 | }, 35 | { 36 | "description": "array of items with no additionalItems", 37 | "schema": { 38 | "items": [{}, {}, {}], 39 | "additionalItems": false 40 | }, 41 | "tests": [ 42 | { 43 | "description": "fewer number of items present", 44 | "data": [ 1, 2 ], 45 | "valid": true 46 | }, 47 | { 48 | "description": "equal number of items present", 49 | "data": [ 1, 2, 3 ], 50 | "valid": true 51 | }, 52 | { 53 | "description": "additional items are not permitted", 54 | "data": [ 1, 2, 3, 4 ], 55 | "valid": false 56 | } 57 | ] 58 | }, 59 | { 60 | "description": "additionalItems as false without items", 61 | "schema": {"additionalItems": false}, 62 | "tests": [ 63 | { 64 | "description": 65 | "items defaults to empty schema so everything is valid", 66 | "data": [ 1, 2, 3, 4, 5 ], 67 | "valid": true 68 | }, 69 | { 70 | "description": "ignores non-arrays", 71 | "data": {"foo" : "bar"}, 72 | "valid": true 73 | } 74 | ] 75 | }, 76 | { 77 | "description": "additionalItems are allowed by default", 78 | "schema": {"items": [{"type": "integer"}]}, 79 | "tests": [ 80 | { 81 | "description": "only the first item is validated", 82 | "data": [1, "foo", false], 83 | "valid": true 84 | } 85 | ] 86 | } 87 | ] 88 | -------------------------------------------------------------------------------- /test/fixtures/schemas/additionalProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": 4 | "additionalProperties being false does not allow other properties", 5 | "schema": { 6 | "properties": {"foo": {}, "bar": {}}, 7 | "patternProperties": { "^v": {} }, 8 | "additionalProperties": false 9 | }, 10 | "tests": [ 11 | { 12 | "description": "no additional properties is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "an additional property is invalid", 18 | "data": {"foo" : 1, "bar" : 2, "quux" : "boom"}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [1, 2, 3], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "foobarbaz", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | }, 36 | { 37 | "description": "patternProperties are not additional properties", 38 | "data": {"foo":1, "vroom": 2}, 39 | "valid": true 40 | } 41 | ] 42 | }, 43 | { 44 | "description": 45 | "additionalProperties allows a schema which should validate", 46 | "schema": { 47 | "properties": {"foo": {}, "bar": {}}, 48 | "additionalProperties": {"type": "boolean"} 49 | }, 50 | "tests": [ 51 | { 52 | "description": "no additional properties is valid", 53 | "data": {"foo": 1}, 54 | "valid": true 55 | }, 56 | { 57 | "description": "an additional valid property is valid", 58 | "data": {"foo" : 1, "bar" : 2, "quux" : true}, 59 | "valid": true 60 | }, 61 | { 62 | "description": "an additional invalid property is invalid", 63 | "data": {"foo" : 1, "bar" : 2, "quux" : 12}, 64 | "valid": false 65 | } 66 | ] 67 | }, 68 | { 69 | "description": 70 | "additionalProperties can exist by itself", 71 | "schema": { 72 | "additionalProperties": {"type": "boolean"} 73 | }, 74 | "tests": [ 75 | { 76 | "description": "an additional valid property is valid", 77 | "data": {"foo" : true}, 78 | "valid": true 79 | }, 80 | { 81 | "description": "an additional invalid property is invalid", 82 | "data": {"foo" : 1}, 83 | "valid": false 84 | } 85 | ] 86 | }, 87 | { 88 | "description": "additionalProperties are allowed by default", 89 | "schema": {"properties": {"foo": {}, "bar": {}}}, 90 | "tests": [ 91 | { 92 | "description": "additional properties are allowed", 93 | "data": {"foo": 1, "bar": 2, "quux": true}, 94 | "valid": true 95 | } 96 | ] 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /test/fixtures/schemas/additionalWithPattern.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "additional properties in combination with patternProperties", 3 | "schema": { 4 | "$schema": "http://json-schema.org/draft-04/schema#", 5 | "type": "array", 6 | "items": { 7 | "title": "Product", 8 | "type": "object", 9 | "patternProperties": { 10 | "^abc": { 11 | "type": "object", 12 | "properties": { 13 | "name": { 14 | "type": "string" 15 | } 16 | }, 17 | "required": ["name"] 18 | }, 19 | "^abc(.+)?123$": { 20 | "type": "object", 21 | "properties": { 22 | "name": { 23 | "type": "string", 24 | "pattern": "^\\w+\\s\\w+$" 25 | } 26 | }, 27 | "required": ["name"] 28 | } 29 | }, 30 | "additionalProperties": false, 31 | "properties": { 32 | "id": { 33 | "type": "number" 34 | } 35 | }, 36 | "required": ["id"] 37 | } 38 | }, 39 | "tests": [{ 40 | "description": "valid array", 41 | "data": [{ 42 | "id": 2 43 | }, { 44 | "id": 3 45 | }], 46 | "valid": true 47 | }, { 48 | "description": "not array", 49 | "data": 1, 50 | "valid": false 51 | }, { 52 | "description": "array of not onjects", 53 | "data": [1, 2, 3], 54 | "valid": false 55 | }, { 56 | "description": "missing required properties", 57 | "data": [{}], 58 | "valid": false 59 | }, { 60 | "description": "additional property added", 61 | "data": [{ 62 | "id": 1, 63 | "name": null 64 | }], 65 | "valid": false 66 | }, { 67 | "description": "additional property but matching pattern, but not valid according to pattern schema", 68 | "data": [{ 69 | "id": 1, 70 | "abc": null 71 | }], 72 | "valid": false 73 | }, { 74 | "description": "additional property but matching pattern, AND valid according to one pattern schema", 75 | "data": [{ 76 | "id": 1, 77 | "abc": { 78 | "name": "my name" 79 | } 80 | }], 81 | "valid": true 82 | }, { 83 | "description": "additional property but matching pattern, AND invalid according to one pattern schema", 84 | "data": [{ 85 | "id": 1, 86 | "abc123": { 87 | "name": "John" 88 | } 89 | }], 90 | "valid": false 91 | }, { 92 | "description": "additional property but matching pattern, AND valid according to two pattern schemas", 93 | "data": [{ 94 | "id": 1, 95 | "abc----123": { 96 | "name": "John Peterson" 97 | } 98 | }], 99 | "valid": true 100 | }] 101 | }, { 102 | "description": "additional properties in combination with patternProperties as schema", 103 | "schema": { 104 | "$schema": "http://json-schema.org/draft-04/schema#", 105 | "type": "array", 106 | "items": { 107 | "title": "Product", 108 | "type": "object", 109 | "patternProperties": { 110 | "^abc": { 111 | "type": "object", 112 | "properties": { 113 | "name": { 114 | "type": "string" 115 | } 116 | }, 117 | "required": ["name"] 118 | }, 119 | "^abc(.+)?123$": { 120 | "type": "object", 121 | "properties": { 122 | "name": { 123 | "type": "string", 124 | "pattern": "^\\w+\\s\\w+$" 125 | } 126 | }, 127 | "required": ["name"] 128 | } 129 | }, 130 | "additionalProperties": { 131 | "type": "number", 132 | "minimum": 10 133 | }, 134 | "properties": { 135 | "id": { 136 | "type": "number" 137 | } 138 | }, 139 | "required": ["id"] 140 | } 141 | }, 142 | "tests": [{ 143 | "description": "valid array", 144 | "data": [{ 145 | "id": 2 146 | }, { 147 | "id": 3 148 | }], 149 | "valid": true 150 | }, { 151 | "description": "additional property added, not matching pattern or additionalProperties", 152 | "data": [{ 153 | "id": 1, 154 | "name": null 155 | }], 156 | "valid": false 157 | }, { 158 | "description": "additional property added, AND additionalProperties matching", 159 | "data": [{ 160 | "id": 1, 161 | "name": 10 162 | }], 163 | "valid": true 164 | }, { 165 | "description": "additional property but matching pattern, but not valid according to pattern schema", 166 | "data": [{ 167 | "id": 1, 168 | "abc": null 169 | }], 170 | "valid": false 171 | }, { 172 | "description": "additional property but matching pattern, AND valid according to one pattern schema", 173 | "data": [{ 174 | "id": 1, 175 | "abc": { 176 | "name": "my name" 177 | } 178 | }], 179 | "valid": true 180 | }, { 181 | "description": "additional property but matching pattern, AND invalid according to one pattern schema", 182 | "data": [{ 183 | "id": 1, 184 | "abc123": { 185 | "name": "John" 186 | } 187 | }], 188 | "valid": false 189 | }, { 190 | "description": "additional property but matching pattern, AND valid according to two pattern schemas", 191 | "data": [{ 192 | "id": 1, 193 | "abc----123": { 194 | "name": "John Peterson" 195 | } 196 | }], 197 | "valid": true 198 | }] 199 | }] 200 | -------------------------------------------------------------------------------- /test/fixtures/schemas/advanced.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "advanced schema from z-schema benchmark (https://github.com/zaggino/z-schema)", 3 | "schema": { 4 | "$schema": "http://json-schema.org/draft-04/schema#", 5 | "type": "object", 6 | "properties": { 7 | "/": { 8 | "$ref": "#/definitions/entry" 9 | } 10 | }, 11 | "patternProperties": { 12 | "^(/[^/]+)+$": { 13 | "$ref": "#/definitions/entry" 14 | } 15 | }, 16 | "additionalProperties": false, 17 | "required": ["/"], 18 | "definitions": { 19 | "entry": { 20 | "$schema": "http://json-schema.org/draft-04/schema#", 21 | "description": "schema for an fstab entry", 22 | "type": "object", 23 | "required": ["storage"], 24 | "properties": { 25 | "storage": { 26 | "type": "object", 27 | "oneOf": [{ 28 | "$ref": "#/definitions/entry/definitions/diskDevice" 29 | }, 30 | { 31 | "$ref": "#/definitions/entry/definitions/diskUUID" 32 | }, 33 | { 34 | "$ref": "#/definitions/entry/definitions/nfs" 35 | }, 36 | { 37 | "$ref": "#/definitions/entry/definitions/tmpfs" 38 | } 39 | ] 40 | }, 41 | "fstype": { 42 | "enum": ["ext3", "ext4", "btrfs"] 43 | }, 44 | "options": { 45 | "type": "array", 46 | "minItems": 1, 47 | "items": { 48 | "type": "string" 49 | }, 50 | "uniqueItems": true 51 | }, 52 | "readonly": { 53 | "type": "boolean" 54 | } 55 | }, 56 | "definitions": { 57 | "diskDevice": { 58 | "properties": { 59 | "type": { 60 | "enum": ["disk"] 61 | }, 62 | "device": { 63 | "type": "string", 64 | "pattern": "^/dev/[^/]+(/[^/]+)*$" 65 | } 66 | }, 67 | "required": ["type", "device"], 68 | "additionalProperties": false 69 | }, 70 | "diskUUID": { 71 | "properties": { 72 | "type": { 73 | "enum": ["disk"] 74 | }, 75 | "label": { 76 | "type": "string", 77 | "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$" 78 | } 79 | }, 80 | "required": ["type", "label"], 81 | "additionalProperties": false 82 | }, 83 | "nfs": { 84 | "properties": { 85 | "type": { 86 | "enum": ["nfs"] 87 | }, 88 | "remotePath": { 89 | "type": "string", 90 | "pattern": "^(/[^/]+)+$" 91 | }, 92 | "server": { 93 | "type": "string", 94 | "anyOf": [{ 95 | "format": "hostname" 96 | }, 97 | { 98 | "format": "ipv4" 99 | }, 100 | { 101 | "format": "ipv6" 102 | } 103 | ] 104 | } 105 | }, 106 | "required": ["type", "server", "remotePath"], 107 | "additionalProperties": false 108 | }, 109 | "tmpfs": { 110 | "properties": { 111 | "type": { 112 | "enum": ["tmpfs"] 113 | }, 114 | "sizeInMB": { 115 | "type": "integer", 116 | "minimum": 16, 117 | "maximum": 512 118 | } 119 | }, 120 | "required": ["type", "sizeInMB"], 121 | "additionalProperties": false 122 | } 123 | } 124 | } 125 | } 126 | }, 127 | "tests": [{ 128 | "description": "valid object from z-schema benchmark", 129 | "data": { 130 | "/": { 131 | "storage": { 132 | "type": "disk", 133 | "device": "/dev/sda1" 134 | }, 135 | "fstype": "btrfs", 136 | "readonly": true 137 | }, 138 | "/var": { 139 | "storage": { 140 | "type": "disk", 141 | "label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f" 142 | }, 143 | "fstype": "ext4", 144 | "options": ["nosuid"] 145 | }, 146 | "/tmp": { 147 | "storage": { 148 | "type": "tmpfs", 149 | "sizeInMB": 64 150 | } 151 | }, 152 | "/var/www": { 153 | "storage": { 154 | "type": "nfs", 155 | "server": "my.nfs.server", 156 | "remotePath": "/exports/mypath" 157 | } 158 | } 159 | }, 160 | "valid": true 161 | }, 162 | { 163 | "description": "not object", 164 | "data": 1, 165 | "valid": false 166 | }, 167 | { 168 | "description": "root only is valid", 169 | "data": { 170 | "/": { 171 | "storage": { 172 | "type": "disk", 173 | "device": "/dev/sda1" 174 | }, 175 | "fstype": "btrfs", 176 | "readonly": true 177 | } 178 | }, 179 | "valid": true 180 | }, 181 | { 182 | "description": "missing root entry", 183 | "data": { 184 | "no root/": { 185 | "storage": { 186 | "type": "disk", 187 | "device": "/dev/sda1" 188 | }, 189 | "fstype": "btrfs", 190 | "readonly": true 191 | } 192 | }, 193 | "valid": false 194 | }, 195 | { 196 | "description": "invalid entry key", 197 | "data": { 198 | "/": { 199 | "storage": { 200 | "type": "disk", 201 | "device": "/dev/sda1" 202 | }, 203 | "fstype": "btrfs", 204 | "readonly": true 205 | }, 206 | "invalid/var": { 207 | "storage": { 208 | "type": "disk", 209 | "label": "8f3ba6f4-5c70-46ec-83af-0d5434953e5f" 210 | }, 211 | "fstype": "ext4", 212 | "options": ["nosuid"] 213 | } 214 | }, 215 | "valid": false 216 | }, 217 | { 218 | "description": "missing storage in entry", 219 | "data": { 220 | "/": { 221 | "fstype": "btrfs", 222 | "readonly": true 223 | } 224 | }, 225 | "valid": false 226 | }, 227 | { 228 | "description": "missing storage type", 229 | "data": { 230 | "/": { 231 | "storage": { 232 | "device": "/dev/sda1" 233 | }, 234 | "fstype": "btrfs", 235 | "readonly": true 236 | } 237 | }, 238 | "valid": false 239 | }, 240 | { 241 | "description": "storage type should be a string", 242 | "data": { 243 | "/": { 244 | "storage": { 245 | "type": null, 246 | "device": "/dev/sda1" 247 | }, 248 | "fstype": "btrfs", 249 | "readonly": true 250 | } 251 | }, 252 | "valid": false 253 | }, 254 | { 255 | "description": "storage device should match pattern", 256 | "data": { 257 | "/": { 258 | "storage": { 259 | "type": null, 260 | "device": "invalid/dev/sda1" 261 | }, 262 | "fstype": "btrfs", 263 | "readonly": true 264 | } 265 | }, 266 | "valid": false 267 | } 268 | ] 269 | }] 270 | -------------------------------------------------------------------------------- /test/fixtures/schemas/allOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "allOf", 4 | "schema": { 5 | "allOf": [ 6 | { 7 | "properties": { 8 | "bar": {"type": "integer"} 9 | }, 10 | "required": ["bar"] 11 | }, 12 | { 13 | "properties": { 14 | "foo": {"type": "string"} 15 | }, 16 | "required": ["foo"] 17 | } 18 | ] 19 | }, 20 | "tests": [ 21 | { 22 | "description": "allOf", 23 | "data": {"foo": "baz", "bar": 2}, 24 | "valid": true 25 | }, 26 | { 27 | "description": "mismatch second", 28 | "data": {"foo": "baz"}, 29 | "valid": false 30 | }, 31 | { 32 | "description": "mismatch first", 33 | "data": {"bar": 2}, 34 | "valid": false 35 | }, 36 | { 37 | "description": "wrong type", 38 | "data": {"foo": "baz", "bar": "quux"}, 39 | "valid": false 40 | } 41 | ] 42 | }, 43 | { 44 | "description": "allOf with base schema", 45 | "schema": { 46 | "properties": {"bar": {"type": "integer"}}, 47 | "required": ["bar"], 48 | "allOf" : [ 49 | { 50 | "properties": { 51 | "foo": {"type": "string"} 52 | }, 53 | "required": ["foo"] 54 | }, 55 | { 56 | "properties": { 57 | "baz": {"type": "null"} 58 | }, 59 | "required": ["baz"] 60 | } 61 | ] 62 | }, 63 | "tests": [ 64 | { 65 | "description": "valid", 66 | "data": {"foo": "quux", "bar": 2, "baz": null}, 67 | "valid": true 68 | }, 69 | { 70 | "description": "mismatch base schema", 71 | "data": {"foo": "quux", "baz": null}, 72 | "valid": false 73 | }, 74 | { 75 | "description": "mismatch first allOf", 76 | "data": {"bar": 2, "baz": null}, 77 | "valid": false 78 | }, 79 | { 80 | "description": "mismatch second allOf", 81 | "data": {"foo": "quux", "bar": 2}, 82 | "valid": false 83 | }, 84 | { 85 | "description": "mismatch both", 86 | "data": {"bar": 2}, 87 | "valid": false 88 | } 89 | ] 90 | }, 91 | { 92 | "description": "allOf simple types", 93 | "schema": { 94 | "allOf": [ 95 | {"maximum": 30}, 96 | {"minimum": 20} 97 | ] 98 | }, 99 | "tests": [ 100 | { 101 | "description": "valid", 102 | "data": 25, 103 | "valid": true 104 | }, 105 | { 106 | "description": "mismatch one", 107 | "data": 35, 108 | "valid": false 109 | } 110 | ] 111 | }, 112 | { 113 | "description": "allOf with boolean schemas, all true", 114 | "schema": {"allOf": [true, true]}, 115 | "tests": [ 116 | { 117 | "description": "any value is valid", 118 | "data": "foo", 119 | "valid": true 120 | } 121 | ] 122 | }, 123 | { 124 | "description": "allOf with boolean schemas, some false", 125 | "schema": {"allOf": [true, false]}, 126 | "tests": [ 127 | { 128 | "description": "any value is invalid", 129 | "data": "foo", 130 | "valid": false 131 | } 132 | ] 133 | }, 134 | { 135 | "description": "allOf with boolean schemas, all false", 136 | "schema": {"allOf": [false, false]}, 137 | "tests": [ 138 | { 139 | "description": "any value is invalid", 140 | "data": "foo", 141 | "valid": false 142 | } 143 | ] 144 | } 145 | ] 146 | -------------------------------------------------------------------------------- /test/fixtures/schemas/anyOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "anyOf", 4 | "schema": { 5 | "anyOf": [ 6 | { 7 | "type": "integer" 8 | }, 9 | { 10 | "minimum": 2 11 | } 12 | ] 13 | }, 14 | "tests": [ 15 | { 16 | "description": "first anyOf valid", 17 | "data": 1, 18 | "valid": true 19 | }, 20 | { 21 | "description": "second anyOf valid", 22 | "data": 2.5, 23 | "valid": true 24 | }, 25 | { 26 | "description": "both anyOf valid", 27 | "data": 3, 28 | "valid": true 29 | }, 30 | { 31 | "description": "neither anyOf valid", 32 | "data": 1.5, 33 | "valid": false 34 | } 35 | ] 36 | }, 37 | { 38 | "description": "anyOf with base schema", 39 | "schema": { 40 | "type": "string", 41 | "anyOf" : [ 42 | { 43 | "maxLength": 2 44 | }, 45 | { 46 | "minLength": 4 47 | } 48 | ] 49 | }, 50 | "tests": [ 51 | { 52 | "description": "mismatch base schema", 53 | "data": 3, 54 | "valid": false 55 | }, 56 | { 57 | "description": "one anyOf valid", 58 | "data": "foobar", 59 | "valid": true 60 | }, 61 | { 62 | "description": "both anyOf invalid", 63 | "data": "foo", 64 | "valid": false 65 | } 66 | ] 67 | }, 68 | { 69 | "description": "anyOf with boolean schemas, all true", 70 | "schema": {"anyOf": [true, true]}, 71 | "tests": [ 72 | { 73 | "description": "any value is valid", 74 | "data": "foo", 75 | "valid": true 76 | } 77 | ] 78 | }, 79 | { 80 | "description": "anyOf with boolean schemas, some true", 81 | "schema": {"anyOf": [true, false]}, 82 | "tests": [ 83 | { 84 | "description": "any value is valid", 85 | "data": "foo", 86 | "valid": true 87 | } 88 | ] 89 | }, 90 | { 91 | "description": "anyOf with boolean schemas, all false", 92 | "schema": {"anyOf": [false, false]}, 93 | "tests": [ 94 | { 95 | "description": "any value is invalid", 96 | "data": "foo", 97 | "valid": false 98 | } 99 | ] 100 | } 101 | ] 102 | -------------------------------------------------------------------------------- /test/fixtures/schemas/basic.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "basic schema from z-schema benchmark (https://github.com/zaggino/z-schema)", 3 | "schema": { 4 | "$schema": "http://json-schema.org/draft-04/schema#", 5 | "title": "Product set", 6 | "type": "array", 7 | "items": { 8 | "title": "Product", 9 | "type": "object", 10 | "properties": { 11 | "id": { 12 | "description": "The unique identifier for a product", 13 | "type": "number" 14 | }, 15 | "name": { 16 | "type": "string" 17 | }, 18 | "price": { 19 | "type": "number", 20 | "minimum": 0, 21 | "exclusiveMinimum": true 22 | }, 23 | "tags": { 24 | "type": "array", 25 | "items": { 26 | "type": "string" 27 | }, 28 | "minItems": 1, 29 | "uniqueItems": true 30 | }, 31 | "dimensions": { 32 | "type": "object", 33 | "properties": { 34 | "length": { 35 | "type": "number" 36 | }, 37 | "width": { 38 | "type": "number" 39 | }, 40 | "height": { 41 | "type": "number" 42 | } 43 | }, 44 | "required": ["length", "width", "height"] 45 | } 46 | }, 47 | "required": ["id", "name", "price"] 48 | } 49 | }, 50 | "tests": [{ 51 | "description": "valid array from z-schema benchmark", 52 | "data": [{ 53 | "id": 2, 54 | "name": "An ice sculpture", 55 | "price": 12.50, 56 | "tags": ["cold", "ice"], 57 | "dimensions": { 58 | "length": 7.0, 59 | "width": 12.0, 60 | "height": 9.5 61 | }, 62 | "warehouseLocation": { 63 | "latitude": -78.75, 64 | "longitude": 20.4 65 | } 66 | }, 67 | { 68 | "id": 3, 69 | "name": "A blue mouse", 70 | "price": 25.50, 71 | "dimensions": { 72 | "length": 3.1, 73 | "width": 1.0, 74 | "height": 1.0 75 | }, 76 | "warehouseLocation": { 77 | "latitude": 54.4, 78 | "longitude": -32.7 79 | } 80 | } 81 | ], 82 | "valid": true 83 | }, 84 | { 85 | "description": "not array", 86 | "data": 1, 87 | "valid": false 88 | }, 89 | { 90 | "description": "array of not onjects", 91 | "data": [1, 2, 3], 92 | "valid": false 93 | }, 94 | { 95 | "description": "missing required properties", 96 | "data": [{}], 97 | "valid": false 98 | }, 99 | { 100 | "description": "required property of wrong type", 101 | "data": [{ 102 | "id": 1, 103 | "name": "product", 104 | "price": "not valid" 105 | }], 106 | "valid": false 107 | }, 108 | { 109 | "description": "smallest valid product", 110 | "data": [{ 111 | "id": 1, 112 | "name": "product", 113 | "price": 100 114 | }], 115 | "valid": true 116 | }, 117 | { 118 | "description": "tags should be array", 119 | "data": [{ 120 | "tags": {}, 121 | "id": 1, 122 | "name": "product", 123 | "price": 100 124 | }], 125 | "valid": false 126 | }, 127 | { 128 | "description": "dimensions should be object", 129 | "data": [{ 130 | "dimensions": [], 131 | "id": 1, 132 | "name": "product", 133 | "price": 100 134 | }], 135 | "valid": false 136 | }, 137 | { 138 | "description": "valid product with tag", 139 | "data": [{ 140 | "tags": ["product"], 141 | "id": 1, 142 | "name": "product", 143 | "price": 100 144 | }], 145 | "valid": true 146 | }, 147 | { 148 | "description": "dimensions miss required properties", 149 | "data": [{ 150 | "dimensions": {}, 151 | "tags": ["product"], 152 | "id": 1, 153 | "name": "product", 154 | "price": 100 155 | }], 156 | "valid": false 157 | }, 158 | { 159 | "description": "valid product with tag and dimensions", 160 | "data": [{ 161 | "dimensions": { 162 | "length": 7, 163 | "width": 12, 164 | "height": 9.5 165 | }, 166 | "tags": ["product"], 167 | "id": 1, 168 | "name": "product", 169 | "price": 100 170 | }], 171 | "valid": true 172 | } 173 | ] 174 | }] 175 | -------------------------------------------------------------------------------- /test/fixtures/schemas/boolean_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "boolean schema 'true'", 4 | "schema": true, 5 | "tests": [ 6 | { 7 | "description": "number is valid", 8 | "data": 1, 9 | "valid": true 10 | }, 11 | { 12 | "description": "string is valid", 13 | "data": "foo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "boolean true is valid", 18 | "data": true, 19 | "valid": true 20 | }, 21 | { 22 | "description": "boolean false is valid", 23 | "data": false, 24 | "valid": true 25 | }, 26 | { 27 | "description": "null is valid", 28 | "data": null, 29 | "valid": true 30 | }, 31 | { 32 | "description": "object is valid", 33 | "data": {"foo": "bar"}, 34 | "valid": true 35 | }, 36 | { 37 | "description": "empty object is valid", 38 | "data": {}, 39 | "valid": true 40 | }, 41 | { 42 | "description": "array is valid", 43 | "data": ["foo"], 44 | "valid": true 45 | }, 46 | { 47 | "description": "empty array is valid", 48 | "data": [], 49 | "valid": true 50 | } 51 | ] 52 | }, 53 | { 54 | "description": "boolean schema 'false'", 55 | "schema": false, 56 | "tests": [ 57 | { 58 | "description": "number is invalid", 59 | "data": 1, 60 | "valid": false 61 | }, 62 | { 63 | "description": "string is invalid", 64 | "data": "foo", 65 | "valid": false 66 | }, 67 | { 68 | "description": "boolean true is invalid", 69 | "data": true, 70 | "valid": false 71 | }, 72 | { 73 | "description": "boolean false is invalid", 74 | "data": false, 75 | "valid": false 76 | }, 77 | { 78 | "description": "null is invalid", 79 | "data": null, 80 | "valid": false 81 | }, 82 | { 83 | "description": "object is invalid", 84 | "data": {"foo": "bar"}, 85 | "valid": false 86 | }, 87 | { 88 | "description": "empty object is invalid", 89 | "data": {}, 90 | "valid": false 91 | }, 92 | { 93 | "description": "array is invalid", 94 | "data": ["foo"], 95 | "valid": false 96 | }, 97 | { 98 | "description": "empty array is invalid", 99 | "data": [], 100 | "valid": false 101 | } 102 | ] 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /test/fixtures/schemas/const.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "const validation", 4 | "schema": {"const": 2}, 5 | "tests": [ 6 | { 7 | "description": "same value is valid", 8 | "data": 2, 9 | "valid": true 10 | }, 11 | { 12 | "description": "another value is invalid", 13 | "data": 5, 14 | "valid": false 15 | }, 16 | { 17 | "description": "another type is invalid", 18 | "data": "a", 19 | "valid": false 20 | } 21 | ] 22 | }, 23 | { 24 | "description": "const with object", 25 | "schema": {"const": {"foo": "bar", "baz": "bax"}}, 26 | "tests": [ 27 | { 28 | "description": "same object is valid", 29 | "data": {"foo": "bar", "baz": "bax"}, 30 | "valid": true 31 | }, 32 | { 33 | "description": "same object with different property order is valid", 34 | "data": {"baz": "bax", "foo": "bar"}, 35 | "valid": true 36 | }, 37 | { 38 | "description": "another object is invalid", 39 | "data": {"foo": "bar"}, 40 | "valid": false 41 | }, 42 | { 43 | "description": "another type is invalid", 44 | "data": [1, 2], 45 | "valid": false 46 | } 47 | ] 48 | }, 49 | { 50 | "description": "const with array", 51 | "schema": {"const": [{ "foo": "bar" }]}, 52 | "tests": [ 53 | { 54 | "description": "same array is valid", 55 | "data": [{"foo": "bar"}], 56 | "valid": true 57 | }, 58 | { 59 | "description": "another array item is invalid", 60 | "data": [2], 61 | "valid": false 62 | }, 63 | { 64 | "description": "array with additional items is invalid", 65 | "data": [1, 2, 3], 66 | "valid": false 67 | } 68 | ] 69 | }, 70 | { 71 | "description": "const with null", 72 | "schema": {"const": null}, 73 | "tests": [ 74 | { 75 | "description": "null is valid", 76 | "data": null, 77 | "valid": true 78 | }, 79 | { 80 | "description": "not null is invalid", 81 | "data": 0, 82 | "valid": false 83 | } 84 | ] 85 | } 86 | ] 87 | -------------------------------------------------------------------------------- /test/fixtures/schemas/contains.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "contains keyword validation", 4 | "schema": { 5 | "contains": {"minimum": 5} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "array with item matching schema (5) is valid", 10 | "data": [3, 4, 5], 11 | "valid": true 12 | }, 13 | { 14 | "description": "array with item matching schema (6) is valid", 15 | "data": [3, 4, 6], 16 | "valid": true 17 | }, 18 | { 19 | "description": "array with two items matching schema (5, 6) is valid", 20 | "data": [3, 4, 5, 6], 21 | "valid": true 22 | }, 23 | { 24 | "description": "array without items matching schema is invalid", 25 | "data": [2, 3, 4], 26 | "valid": false 27 | }, 28 | { 29 | "description": "empty array is invalid", 30 | "data": [], 31 | "valid": false 32 | }, 33 | { 34 | "description": "not array is valid", 35 | "data": {}, 36 | "valid": true 37 | } 38 | ] 39 | }, 40 | { 41 | "description": "contains keyword with const keyword", 42 | "schema": { 43 | "contains": { "const": 5 } 44 | }, 45 | "tests": [ 46 | { 47 | "description": "array with item 5 is valid", 48 | "data": [3, 4, 5], 49 | "valid": true 50 | }, 51 | { 52 | "description": "array with two items 5 is valid", 53 | "data": [3, 4, 5, 5], 54 | "valid": true 55 | }, 56 | { 57 | "description": "array without item 5 is invalid", 58 | "data": [1, 2, 3, 4], 59 | "valid": false 60 | } 61 | ] 62 | }, 63 | { 64 | "description": "contains keyword with boolean schema true", 65 | "schema": {"contains": true}, 66 | "tests": [ 67 | { 68 | "description": "any non-empty array is valid", 69 | "data": ["foo"], 70 | "valid": true 71 | }, 72 | { 73 | "description": "empty array is invalid", 74 | "data": [], 75 | "valid": false 76 | } 77 | ] 78 | }, 79 | { 80 | "description": "contains keyword with boolean schema false", 81 | "schema": {"contains": false}, 82 | "tests": [ 83 | { 84 | "description": "any non-empty array is invalid", 85 | "data": ["foo"], 86 | "valid": false 87 | }, 88 | { 89 | "description": "empty array is invalid", 90 | "data": [], 91 | "valid": false 92 | } 93 | ] 94 | } 95 | ] 96 | -------------------------------------------------------------------------------- /test/fixtures/schemas/cosmicrealms.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "schema from cosmicrealms benchmark", 3 | "schema": { 4 | "name": "test", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "required": [ 8 | "fullName", "age", "zip", "married", 9 | "dozen", "dozenOrBakersDozen", 10 | "favoriteEvenNumber", "topThreeFavoriteColors", 11 | "favoriteSingleDigitWholeNumbers", "favoriteFiveLetterWord", 12 | "emailAddresses", "ipAddresses" 13 | ], 14 | "properties": { 15 | "fullName": { 16 | "type": "string" 17 | }, 18 | "age": { 19 | "type": "integer", 20 | "minimum": 0 21 | }, 22 | "optionalItem": { 23 | "type": "string" 24 | }, 25 | "state": { 26 | "type": "string" 27 | }, 28 | "city": { 29 | "type": "string" 30 | }, 31 | "zip": { 32 | "type": "integer", 33 | "minimum": 0, 34 | "maximum": 99999 35 | }, 36 | "married": { 37 | "type": "boolean" 38 | }, 39 | "dozen": { 40 | "type": "integer", 41 | "minimum": 12, 42 | "maximum": 12 43 | }, 44 | "dozenOrBakersDozen": { 45 | "type": "integer", 46 | "minimum": 12, 47 | "maximum": 13 48 | }, 49 | "favoriteEvenNumber": { 50 | "type": "integer", 51 | "multipleOf": 2 52 | }, 53 | "topThreeFavoriteColors": { 54 | "type": "array", 55 | "minItems": 3, 56 | "maxItems": 3, 57 | "uniqueItems": true, 58 | "items": { 59 | "type": "string" 60 | } 61 | }, 62 | "favoriteSingleDigitWholeNumbers": { 63 | "type": "array", 64 | "minItems": 1, 65 | "maxItems": 10, 66 | "uniqueItems": true, 67 | "items": { 68 | "type": "integer", 69 | "minimum": 0, 70 | "maximum": 9 71 | } 72 | }, 73 | "favoriteFiveLetterWord": { 74 | "type": "string", 75 | "minLength": 5, 76 | "maxLength": 5 77 | }, 78 | "emailAddresses": { 79 | "type": "array", 80 | "minItems": 1, 81 | "uniqueItems": true, 82 | "items": { 83 | "type": "string", 84 | "format": "email" 85 | } 86 | }, 87 | "ipAddresses": { 88 | "type": "array", 89 | "uniqueItems": true, 90 | "items": { 91 | "type": "string", 92 | "format": "ipv4" 93 | } 94 | } 95 | } 96 | }, 97 | "tests": [{ 98 | "description": "valid data from cosmicrealms benchmark", 99 | "data": { 100 | "fullName": "John Smith", 101 | "state": "CA", 102 | "city": "Los Angeles", 103 | "favoriteFiveLetterWord": "hello", 104 | "emailAddresses": [ 105 | "NRorsfCYtvB5bKAf1jZMu1GAJzAhhg5lEvh@inTqnn.net", 106 | "6tjWtYxjaan2Ivm5QZVhKxImKawRCA6gcqtMEwV1@bB01pCtIBY0F.org", 107 | "j68UnHfrHiKwpAm8iYokoMuRTpWUj8bfxspusNFK@COoWeMZL.edu", 108 | "qlnrIsYSWCGUQW6f8HL@UBOqUYQQzugVL.uk" 109 | ], 110 | "dozen": 12, 111 | "dozenOrBakersDozen": 13, 112 | "favoriteEvenNumber": 24, 113 | "married": true, 114 | "age": 17, 115 | "zip": 65794, 116 | "topThreeFavoriteColors": [ 117 | "blue", 118 | "black", 119 | "yellow" 120 | ], 121 | "favoriteSingleDigitWholeNumbers": [ 122 | 2, 123 | 1, 124 | 3, 125 | 9 126 | ], 127 | "ipAddresses": [ 128 | "225.234.40.3", 129 | "96.216.243.54", 130 | "18.126.145.83", 131 | "196.17.191.239" 132 | ] 133 | }, 134 | "valid": true 135 | }, 136 | { 137 | "description": "invalid data", 138 | "data": { 139 | "state": null, 140 | "city": 90912, 141 | "zip": [null], 142 | "married": "married", 143 | "dozen": 90912, 144 | "dozenOrBakersDozen": null, 145 | "favoriteEvenNumber": -1294145, 146 | "emailAddresses": [], 147 | "topThreeFavoriteColors": [ 148 | null, 149 | null, 150 | 0.7925170068027211, 151 | 1.2478632478632479, 152 | 1.173913043478261, 153 | 0.4472049689440994 154 | ], 155 | "favoriteSingleDigitWholeNumbers": [], 156 | "favoriteFiveLetterWord": "more than five letters", 157 | "ipAddresses": [ 158 | "55.335.74.758", 159 | "191.266.92.805", 160 | "193.388.390.250", 161 | "269.375.318.49", 162 | "120.268.59.140" 163 | ] 164 | }, 165 | "valid": false 166 | } 167 | ] 168 | }] 169 | -------------------------------------------------------------------------------- /test/fixtures/schemas/default.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "invalid type for default", 4 | "schema": { 5 | "properties": { 6 | "foo": { 7 | "type": "integer", 8 | "default": [] 9 | } 10 | } 11 | }, 12 | "tests": [ 13 | { 14 | "description": "valid when property is specified", 15 | "data": {"foo": 13}, 16 | "valid": true 17 | }, 18 | { 19 | "description": "still valid when the invalid default is used", 20 | "data": {}, 21 | "valid": true 22 | } 23 | ] 24 | }, 25 | { 26 | "description": "invalid string value for default", 27 | "schema": { 28 | "properties": { 29 | "bar": { 30 | "type": "string", 31 | "minLength": 4, 32 | "default": "bad" 33 | } 34 | } 35 | }, 36 | "tests": [ 37 | { 38 | "description": "valid when property is specified", 39 | "data": {"bar": "good"}, 40 | "valid": true 41 | }, 42 | { 43 | "description": "still valid when the invalid default is used", 44 | "data": {}, 45 | "valid": true 46 | } 47 | ] 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /test/fixtures/schemas/definitions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "valid definition", 4 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, 5 | "tests": [ 6 | { 7 | "description": "valid definition schema", 8 | "data": { 9 | "definitions": { 10 | "foo": {"type": "integer"} 11 | } 12 | }, 13 | "valid": true 14 | } 15 | ] 16 | }, 17 | { 18 | "description": "invalid definition", 19 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, 20 | "tests": [ 21 | { 22 | "description": "invalid definition schema", 23 | "data": { 24 | "definitions": { 25 | "foo": {"type": 1} 26 | } 27 | }, 28 | "valid": false 29 | } 30 | ] 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /test/fixtures/schemas/dependencies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "dependencies", 4 | "schema": { 5 | "dependencies": {"bar": ["foo"]} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "neither", 10 | "data": {}, 11 | "valid": true 12 | }, 13 | { 14 | "description": "nondependant", 15 | "data": {"foo": 1}, 16 | "valid": true 17 | }, 18 | { 19 | "description": "with dependency", 20 | "data": {"foo": 1, "bar": 2}, 21 | "valid": true 22 | }, 23 | { 24 | "description": "missing dependency", 25 | "data": {"bar": 2}, 26 | "valid": false 27 | }, 28 | { 29 | "description": "ignores arrays", 30 | "data": ["bar"], 31 | "valid": true 32 | }, 33 | { 34 | "description": "ignores strings", 35 | "data": "foobar", 36 | "valid": true 37 | }, 38 | { 39 | "description": "ignores other non-objects", 40 | "data": 12, 41 | "valid": true 42 | } 43 | ] 44 | }, 45 | { 46 | "description": "dependencies with empty array", 47 | "schema": { 48 | "dependencies": {"bar": []} 49 | }, 50 | "tests": [ 51 | { 52 | "description": "empty object", 53 | "data": {}, 54 | "valid": true 55 | }, 56 | { 57 | "description": "object with one property", 58 | "data": {"bar": 2}, 59 | "valid": true 60 | } 61 | ] 62 | }, 63 | { 64 | "description": "multiple dependencies", 65 | "schema": { 66 | "dependencies": {"quux": ["foo", "bar"]} 67 | }, 68 | "tests": [ 69 | { 70 | "description": "neither", 71 | "data": {}, 72 | "valid": true 73 | }, 74 | { 75 | "description": "nondependants", 76 | "data": {"foo": 1, "bar": 2}, 77 | "valid": true 78 | }, 79 | { 80 | "description": "with dependencies", 81 | "data": {"foo": 1, "bar": 2, "quux": 3}, 82 | "valid": true 83 | }, 84 | { 85 | "description": "missing dependency", 86 | "data": {"foo": 1, "quux": 2}, 87 | "valid": false 88 | }, 89 | { 90 | "description": "missing other dependency", 91 | "data": {"bar": 1, "quux": 2}, 92 | "valid": false 93 | }, 94 | { 95 | "description": "missing both dependencies", 96 | "data": {"quux": 1}, 97 | "valid": false 98 | } 99 | ] 100 | }, 101 | { 102 | "description": "multiple dependencies subschema", 103 | "schema": { 104 | "dependencies": { 105 | "bar": { 106 | "properties": { 107 | "foo": {"type": "integer"}, 108 | "bar": {"type": "integer"} 109 | } 110 | } 111 | } 112 | }, 113 | "tests": [ 114 | { 115 | "description": "valid", 116 | "data": {"foo": 1, "bar": 2}, 117 | "valid": true 118 | }, 119 | { 120 | "description": "no dependency", 121 | "data": {"foo": "quux"}, 122 | "valid": true 123 | }, 124 | { 125 | "description": "wrong type", 126 | "data": {"foo": "quux", "bar": 2}, 127 | "valid": false 128 | }, 129 | { 130 | "description": "wrong type other", 131 | "data": {"foo": 2, "bar": "quux"}, 132 | "valid": false 133 | }, 134 | { 135 | "description": "wrong type both", 136 | "data": {"foo": "quux", "bar": "quux"}, 137 | "valid": false 138 | } 139 | ] 140 | }, 141 | { 142 | "description": "dependencies with boolean subschemas", 143 | "schema": { 144 | "dependencies": { 145 | "foo": true, 146 | "bar": false 147 | } 148 | }, 149 | "tests": [ 150 | { 151 | "description": "object with property having schema true is valid", 152 | "data": {"foo": 1}, 153 | "valid": true 154 | }, 155 | { 156 | "description": "object with property having schema false is invalid", 157 | "data": {"bar": 2}, 158 | "valid": false 159 | }, 160 | { 161 | "description": "object with both properties is invalid", 162 | "data": {"foo": 1, "bar": 2}, 163 | "valid": false 164 | }, 165 | { 166 | "description": "empty object is valid", 167 | "data": {}, 168 | "valid": true 169 | } 170 | ] 171 | } 172 | ] 173 | -------------------------------------------------------------------------------- /test/fixtures/schemas/enum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "simple enum validation", 4 | "schema": {"enum": [1, 2, 3]}, 5 | "tests": [ 6 | { 7 | "description": "one of the enum is valid", 8 | "data": 1, 9 | "valid": true 10 | }, 11 | { 12 | "description": "something else is invalid", 13 | "data": 4, 14 | "valid": false 15 | } 16 | ] 17 | }, 18 | { 19 | "description": "heterogeneous enum validation", 20 | "schema": {"enum": [6, "foo", [], true, {"foo": 12}]}, 21 | "tests": [ 22 | { 23 | "description": "one of the enum is valid", 24 | "data": [], 25 | "valid": true 26 | }, 27 | { 28 | "description": "something else is invalid", 29 | "data": null, 30 | "valid": false 31 | }, 32 | { 33 | "description": "objects are deep compared", 34 | "data": {"foo": false}, 35 | "valid": false 36 | } 37 | ] 38 | }, 39 | { 40 | "description": "enums in properties", 41 | "schema": { 42 | "type":"object", 43 | "properties": { 44 | "foo": {"enum":["foo"]}, 45 | "bar": {"enum":["bar"]} 46 | }, 47 | "required": ["bar"] 48 | }, 49 | "tests": [ 50 | { 51 | "description": "both properties are valid", 52 | "data": {"foo":"foo", "bar":"bar"}, 53 | "valid": true 54 | }, 55 | { 56 | "description": "missing optional property is valid", 57 | "data": {"bar":"bar"}, 58 | "valid": true 59 | }, 60 | { 61 | "description": "missing required property is invalid", 62 | "data": {"foo":"foo"}, 63 | "valid": false 64 | }, 65 | { 66 | "description": "missing all properties is invalid", 67 | "data": {}, 68 | "valid": false 69 | } 70 | ] 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /test/fixtures/schemas/exclusiveMaximum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "exclusiveMaximum validation", 4 | "schema": { 5 | "exclusiveMaximum": 3.0 6 | }, 7 | "tests": [ 8 | { 9 | "description": "below the exclusiveMaximum is valid", 10 | "data": 2.2, 11 | "valid": true 12 | }, 13 | { 14 | "description": "boundary point is invalid", 15 | "data": 3.0, 16 | "valid": false 17 | }, 18 | { 19 | "description": "above the exclusiveMaximum is invalid", 20 | "data": 3.5, 21 | "valid": false 22 | }, 23 | { 24 | "description": "ignores non-numbers", 25 | "data": "x", 26 | "valid": true 27 | } 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/fixtures/schemas/exclusiveMinimum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "exclusiveMinimum validation", 4 | "schema": { 5 | "exclusiveMinimum": 1.1 6 | }, 7 | "tests": [ 8 | { 9 | "description": "above the exclusiveMinimum is valid", 10 | "data": 1.2, 11 | "valid": true 12 | }, 13 | { 14 | "description": "boundary point is invalid", 15 | "data": 1.1, 16 | "valid": false 17 | }, 18 | { 19 | "description": "below the exclusiveMinimum is invalid", 20 | "data": 0.6, 21 | "valid": false 22 | }, 23 | { 24 | "description": "ignores non-numbers", 25 | "data": "x", 26 | "valid": true 27 | } 28 | ] 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /test/fixtures/schemas/items.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "a schema given for items", 4 | "schema": { 5 | "items": {"type": "integer"} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "valid items", 10 | "data": [ 1, 2, 3 ], 11 | "valid": true 12 | }, 13 | { 14 | "description": "wrong type of items", 15 | "data": [1, "x"], 16 | "valid": false 17 | }, 18 | { 19 | "description": "ignores non-arrays", 20 | "data": {"foo" : "bar"}, 21 | "valid": true 22 | }, 23 | { 24 | "description": "ignores non-arrays 2", 25 | "data": 1, 26 | "valid": true 27 | }, 28 | { 29 | "description": "ignores non-arrays 3", 30 | "data": false, 31 | "valid": true 32 | }, 33 | { 34 | "description": "ignores non-arrays 4", 35 | "data": null, 36 | "valid": true 37 | } 38 | ] 39 | }, 40 | { 41 | "description": "an array of schemas for items", 42 | "schema": { 43 | "items": [ 44 | {"type": "integer"}, 45 | {"type": "string"} 46 | ] 47 | }, 48 | "tests": [ 49 | { 50 | "description": "correct types", 51 | "data": [ 1, "foo" ], 52 | "valid": true 53 | }, 54 | { 55 | "description": "wrong types", 56 | "data": [ "foo", 1 ], 57 | "valid": false 58 | }, 59 | { 60 | "description": "incomplete array of items", 61 | "data": [ 1 ], 62 | "valid": true 63 | }, 64 | { 65 | "description": "array with additional items", 66 | "data": [ 1, "foo", true ], 67 | "valid": true 68 | }, 69 | { 70 | "description": "empty array", 71 | "data": [ ], 72 | "valid": true 73 | } 74 | ] 75 | }, 76 | { 77 | "description": "items with boolean schema (true)", 78 | "schema": {"items": true}, 79 | "tests": [ 80 | { 81 | "description": "any array is valid", 82 | "data": [ 1, "foo", true ], 83 | "valid": true 84 | }, 85 | { 86 | "description": "empty array is valid", 87 | "data": [], 88 | "valid": true 89 | } 90 | ] 91 | }, 92 | { 93 | "description": "items with boolean schema (false)", 94 | "schema": {"items": false}, 95 | "tests": [ 96 | { 97 | "description": "any non-empty array is invalid", 98 | "data": [ 1, "foo", true ], 99 | "valid": false 100 | }, 101 | { 102 | "description": "empty array is valid", 103 | "data": [], 104 | "valid": true 105 | } 106 | ] 107 | }, 108 | { 109 | "description": "items with boolean schemas", 110 | "schema": { 111 | "items": [true, false] 112 | }, 113 | "tests": [ 114 | { 115 | "description": "array with one item is valid", 116 | "data": [ 1 ], 117 | "valid": true 118 | }, 119 | { 120 | "description": "array with two items is invalid", 121 | "data": [ 1, "foo" ], 122 | "valid": false 123 | }, 124 | { 125 | "description": "empty array is valid", 126 | "data": [], 127 | "valid": true 128 | } 129 | ] 130 | } 131 | ] 132 | -------------------------------------------------------------------------------- /test/fixtures/schemas/maxItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxItems validation", 4 | "schema": {"maxItems": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": [1], 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": [1, 2], 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": [1, 2, 3], 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-arrays", 23 | "data": "foobar", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/fixtures/schemas/maxLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxLength validation", 4 | "schema": {"maxLength": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": "f", 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": "fo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": "foo", 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-strings", 23 | "data": 100, 24 | "valid": true 25 | }, 26 | { 27 | "description": "two supplementary Unicode code points is long enough", 28 | "data": "\uD83D\uDCA9", 29 | "valid": true 30 | } 31 | ] 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /test/fixtures/schemas/maxProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maxProperties validation", 4 | "schema": {"maxProperties": 2}, 5 | "tests": [ 6 | { 7 | "description": "shorter is valid", 8 | "data": {"foo": 1}, 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": {"foo": 1, "bar": 2}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "too long is invalid", 18 | "data": {"foo": 1, "bar": 2, "baz": 3}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [1, 2, 3], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "foobar", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | } 36 | ] 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /test/fixtures/schemas/maximum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "maximum validation", 4 | "schema": {"maximum": 3.0}, 5 | "tests": [ 6 | { 7 | "description": "below the maximum is valid", 8 | "data": 2.6, 9 | "valid": true 10 | }, 11 | { 12 | "description": "boundary point is valid", 13 | "data": 3.0, 14 | "valid": true 15 | }, 16 | { 17 | "description": "above the maximum is invalid", 18 | "data": 3.5, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-numbers", 23 | "data": "x", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/fixtures/schemas/medium.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "description": "medium schema from jsck benchmark (https://github.com/pandastrike/jsck)", 3 | "schema": { 4 | "description": "A moderately complex schema with some nesting and value constraints", 5 | "type": "object", 6 | "additionalProperties": false, 7 | "required": [ 8 | "api_server", 9 | "transport", 10 | "storage", 11 | "chain" 12 | ], 13 | "properties": { 14 | "api_server": { 15 | "description": "Settings for the HTTP API server", 16 | "type": "object", 17 | "additionalProperties": false, 18 | "required": [ 19 | "url", 20 | "host", 21 | "port" 22 | ], 23 | "properties": { 24 | "url": { 25 | "type": "string", 26 | "format": "uri" 27 | }, 28 | "host": { 29 | "type": "string" 30 | }, 31 | "port": { 32 | "type": "integer", 33 | "minimum": 1000 34 | } 35 | } 36 | }, 37 | "transport": { 38 | "description": "Settings for the Redis tranport", 39 | "additionalProperties": false, 40 | "required": [ 41 | "server" 42 | ], 43 | "properties": { 44 | "server": { 45 | "type": "string" 46 | }, 47 | "options": { 48 | "type": "object" 49 | }, 50 | "queues": { 51 | "properties": { 52 | "blocking_timeout": { 53 | "type": "integer", 54 | "minimum": 0 55 | } 56 | } 57 | } 58 | } 59 | }, 60 | "storage": { 61 | "description": "Settings for the PostgreSQL storage", 62 | "required": [ 63 | "server", 64 | "database", 65 | "user" 66 | ], 67 | "properties": { 68 | "server": { 69 | "type": "string" 70 | }, 71 | "database": { 72 | "type": "string" 73 | }, 74 | "user": { 75 | "type": "string" 76 | }, 77 | "options": { 78 | "type": "object" 79 | } 80 | } 81 | }, 82 | "chain": { 83 | "description": "Settings for the Chain.com client", 84 | "required": [ 85 | "api_key_id", 86 | "api_key_secret" 87 | ], 88 | "properties": { 89 | "api_key_id": { 90 | "type": "string" 91 | }, 92 | "api_key_secret": { 93 | "type": "string" 94 | } 95 | } 96 | } 97 | } 98 | }, 99 | "tests": [{ 100 | "description": "valid object from jsck benchmark", 101 | "data": { 102 | "api_server": { 103 | "url": "http://example.com:8998", 104 | "host": "example.com", 105 | "port": 8998 106 | }, 107 | "transport": { 108 | "server": "127.0.0.1:6381", 109 | "queues": { 110 | "blocking_timeout": 0 111 | } 112 | }, 113 | "storage": { 114 | "server": "127.0.0.1:5432", 115 | "database": "thingy-test", 116 | "user": "thingy-test", 117 | "password": "password" 118 | }, 119 | "chain": { 120 | "api_key_id": "cafebabe", 121 | "api_key_secret": "babecafe" 122 | } 123 | }, 124 | "valid": true 125 | }, 126 | { 127 | "description": "not object", 128 | "data": 1, 129 | "valid": false 130 | } 131 | ] 132 | }] 133 | -------------------------------------------------------------------------------- /test/fixtures/schemas/minItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minItems validation", 4 | "schema": {"minItems": 1}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": [1, 2], 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": [1], 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": [], 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-arrays", 23 | "data": "", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/fixtures/schemas/minLength.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minLength validation", 4 | "schema": {"minLength": 2}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": "foo", 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": "fo", 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": "f", 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-strings", 23 | "data": 1, 24 | "valid": true 25 | }, 26 | { 27 | "description": "one supplementary Unicode code point is not long enough", 28 | "data": "\uD83D\uDCA9", 29 | "valid": true 30 | } 31 | ] 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /test/fixtures/schemas/minProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minProperties validation", 4 | "schema": {"minProperties": 1}, 5 | "tests": [ 6 | { 7 | "description": "longer is valid", 8 | "data": {"foo": 1, "bar": 2}, 9 | "valid": true 10 | }, 11 | { 12 | "description": "exact length is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "too short is invalid", 18 | "data": {}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores arrays", 23 | "data": [], 24 | "valid": true 25 | }, 26 | { 27 | "description": "ignores strings", 28 | "data": "", 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores other non-objects", 33 | "data": 12, 34 | "valid": true 35 | } 36 | ] 37 | } 38 | ] 39 | -------------------------------------------------------------------------------- /test/fixtures/schemas/minimum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "minimum validation", 4 | "schema": {"minimum": 1.1}, 5 | "tests": [ 6 | { 7 | "description": "above the minimum is valid", 8 | "data": 2.6, 9 | "valid": true 10 | }, 11 | { 12 | "description": "boundary point is valid", 13 | "data": 1.1, 14 | "valid": true 15 | }, 16 | { 17 | "description": "below the minimum is invalid", 18 | "data": 0.6, 19 | "valid": false 20 | }, 21 | { 22 | "description": "ignores non-numbers", 23 | "data": "x", 24 | "valid": true 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /test/fixtures/schemas/multipleOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "by int", 4 | "schema": {"multipleOf": 2}, 5 | "tests": [ 6 | { 7 | "description": "int by int", 8 | "data": 10, 9 | "valid": true 10 | }, 11 | { 12 | "description": "int by int fail", 13 | "data": 7, 14 | "valid": false 15 | }, 16 | { 17 | "description": "ignores non-numbers", 18 | "data": "foo", 19 | "valid": true 20 | } 21 | ] 22 | }, 23 | { 24 | "description": "by number", 25 | "schema": {"multipleOf": 1.5}, 26 | "tests": [ 27 | { 28 | "description": "zero is multiple of anything", 29 | "data": 0, 30 | "valid": true 31 | }, 32 | { 33 | "description": "4.5 is multiple of 1.5", 34 | "data": 4.5, 35 | "valid": true 36 | }, 37 | { 38 | "description": "35 is not multiple of 1.5", 39 | "data": 35, 40 | "valid": false 41 | } 42 | ] 43 | }, 44 | { 45 | "description": "by small number", 46 | "schema": {"multipleOf": 0.0001}, 47 | "tests": [ 48 | { 49 | "description": "0.0075 is multiple of 0.0001", 50 | "data": 0.0075, 51 | "valid": true 52 | }, 53 | { 54 | "description": "0.00751 is not multiple of 0.0001", 55 | "data": 0.00751, 56 | "valid": false 57 | } 58 | ] 59 | } 60 | ] 61 | -------------------------------------------------------------------------------- /test/fixtures/schemas/not.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "not", 4 | "schema": { 5 | "not": {"type": "integer"} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "allowed", 10 | "data": "foo", 11 | "valid": true 12 | }, 13 | { 14 | "description": "disallowed", 15 | "data": 1, 16 | "valid": false 17 | } 18 | ] 19 | }, 20 | { 21 | "description": "not multiple types", 22 | "schema": { 23 | "not": {"type": ["integer", "boolean"]} 24 | }, 25 | "tests": [ 26 | { 27 | "description": "valid", 28 | "data": "foo", 29 | "valid": true 30 | }, 31 | { 32 | "description": "mismatch", 33 | "data": 1, 34 | "valid": false 35 | }, 36 | { 37 | "description": "other mismatch", 38 | "data": true, 39 | "valid": false 40 | } 41 | ] 42 | }, 43 | { 44 | "description": "not more complex schema", 45 | "schema": { 46 | "not": { 47 | "type": "object", 48 | "properties": { 49 | "foo": { 50 | "type": "string" 51 | } 52 | } 53 | } 54 | }, 55 | "tests": [ 56 | { 57 | "description": "match", 58 | "data": 1, 59 | "valid": true 60 | }, 61 | { 62 | "description": "other match", 63 | "data": {"foo": 1}, 64 | "valid": true 65 | }, 66 | { 67 | "description": "mismatch", 68 | "data": {"foo": "bar"}, 69 | "valid": false 70 | } 71 | ] 72 | }, 73 | { 74 | "description": "forbidden property", 75 | "schema": { 76 | "properties": { 77 | "foo": { 78 | "not": {} 79 | } 80 | } 81 | }, 82 | "tests": [ 83 | { 84 | "description": "property present", 85 | "data": {"foo": 1, "bar": 2}, 86 | "valid": false 87 | }, 88 | { 89 | "description": "property absent", 90 | "data": {"bar": 1, "baz": 2}, 91 | "valid": true 92 | } 93 | ] 94 | }, 95 | { 96 | "description": "not with boolean schema true", 97 | "schema": {"not": true}, 98 | "tests": [ 99 | { 100 | "description": "any value is invalid", 101 | "data": "foo", 102 | "valid": false 103 | } 104 | ] 105 | }, 106 | { 107 | "description": "not with boolean schema false", 108 | "schema": {"not": false}, 109 | "tests": [ 110 | { 111 | "description": "any value is valid", 112 | "data": "foo", 113 | "valid": true 114 | } 115 | ] 116 | } 117 | ] 118 | -------------------------------------------------------------------------------- /test/fixtures/schemas/oneOf.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "oneOf", 4 | "schema": { 5 | "oneOf": [ 6 | { 7 | "type": "integer" 8 | }, 9 | { 10 | "minimum": 2 11 | } 12 | ] 13 | }, 14 | "tests": [ 15 | { 16 | "description": "first oneOf valid", 17 | "data": 1, 18 | "valid": true 19 | }, 20 | { 21 | "description": "second oneOf valid", 22 | "data": 2.5, 23 | "valid": true 24 | }, 25 | { 26 | "description": "both oneOf valid", 27 | "data": 3, 28 | "valid": false 29 | }, 30 | { 31 | "description": "neither oneOf valid", 32 | "data": 1.5, 33 | "valid": false 34 | } 35 | ] 36 | }, 37 | { 38 | "description": "oneOf with base schema", 39 | "schema": { 40 | "type": "string", 41 | "oneOf" : [ 42 | { 43 | "minLength": 2 44 | }, 45 | { 46 | "maxLength": 4 47 | } 48 | ] 49 | }, 50 | "tests": [ 51 | { 52 | "description": "mismatch base schema", 53 | "data": 3, 54 | "valid": false 55 | }, 56 | { 57 | "description": "one oneOf valid", 58 | "data": "foobar", 59 | "valid": true 60 | }, 61 | { 62 | "description": "both oneOf valid", 63 | "data": "foo", 64 | "valid": false 65 | } 66 | ] 67 | }, 68 | { 69 | "description": "oneOf with boolean schemas, all true", 70 | "schema": {"oneOf": [true, true, true]}, 71 | "tests": [ 72 | { 73 | "description": "any value is invalid", 74 | "data": "foo", 75 | "valid": false 76 | } 77 | ] 78 | }, 79 | { 80 | "description": "oneOf with boolean schemas, one true", 81 | "schema": {"oneOf": [true, false, false]}, 82 | "tests": [ 83 | { 84 | "description": "any value is valid", 85 | "data": "foo", 86 | "valid": true 87 | } 88 | ] 89 | }, 90 | { 91 | "description": "oneOf with boolean schemas, more than one true", 92 | "schema": {"oneOf": [true, true, false]}, 93 | "tests": [ 94 | { 95 | "description": "any value is invalid", 96 | "data": "foo", 97 | "valid": false 98 | } 99 | ] 100 | }, 101 | { 102 | "description": "oneOf with boolean schemas, all false", 103 | "schema": {"oneOf": [false, false, false]}, 104 | "tests": [ 105 | { 106 | "description": "any value is invalid", 107 | "data": "foo", 108 | "valid": false 109 | } 110 | ] 111 | } 112 | ] 113 | -------------------------------------------------------------------------------- /test/fixtures/schemas/optional/bignum.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "integer", 4 | "schema": {"type": "integer"}, 5 | "tests": [ 6 | { 7 | "description": "a bignum is an integer", 8 | "data": 12345678910111213141516171819202122232425262728293031, 9 | "valid": true 10 | } 11 | ] 12 | }, 13 | { 14 | "description": "number", 15 | "schema": {"type": "number"}, 16 | "tests": [ 17 | { 18 | "description": "a bignum is a number", 19 | "data": 98249283749234923498293171823948729348710298301928331, 20 | "valid": true 21 | } 22 | ] 23 | }, 24 | { 25 | "description": "integer", 26 | "schema": {"type": "integer"}, 27 | "tests": [ 28 | { 29 | "description": "a negative bignum is an integer", 30 | "data": -12345678910111213141516171819202122232425262728293031, 31 | "valid": true 32 | } 33 | ] 34 | }, 35 | { 36 | "description": "number", 37 | "schema": {"type": "number"}, 38 | "tests": [ 39 | { 40 | "description": "a negative bignum is a number", 41 | "data": -98249283749234923498293171823948729348710298301928331, 42 | "valid": true 43 | } 44 | ] 45 | }, 46 | { 47 | "description": "string", 48 | "schema": {"type": "string"}, 49 | "tests": [ 50 | { 51 | "description": "a bignum is not a string", 52 | "data": 98249283749234923498293171823948729348710298301928331, 53 | "valid": false 54 | } 55 | ] 56 | }, 57 | { 58 | "description": "integer comparison", 59 | "schema": {"maximum": 18446744073709551615}, 60 | "tests": [ 61 | { 62 | "description": "comparison works for high numbers", 63 | "data": 18446744073709551600, 64 | "valid": true 65 | } 66 | ] 67 | }, 68 | { 69 | "description": "float comparison with high precision", 70 | "schema": { 71 | "exclusiveMaximum": 972783798187987123879878123.18878137 72 | }, 73 | "tests": [ 74 | { 75 | "description": "comparison works for high numbers", 76 | "data": 972783798187987123879878123.188781371, 77 | "valid": false 78 | } 79 | ] 80 | }, 81 | { 82 | "description": "integer comparison", 83 | "schema": {"minimum": -18446744073709551615}, 84 | "tests": [ 85 | { 86 | "description": "comparison works for very negative numbers", 87 | "data": -18446744073709551600, 88 | "valid": true 89 | } 90 | ] 91 | }, 92 | { 93 | "description": "float comparison with high precision on negative numbers", 94 | "schema": { 95 | "exclusiveMinimum": -972783798187987123879878123.18878137 96 | }, 97 | "tests": [ 98 | { 99 | "description": "comparison works for very negative numbers", 100 | "data": -972783798187987123879878123.188781371, 101 | "valid": false 102 | } 103 | ] 104 | } 105 | ] 106 | -------------------------------------------------------------------------------- /test/fixtures/schemas/optional/ecmascript-regex.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "ECMA 262 regex non-compliance", 4 | "schema": { "format": "regex" }, 5 | "tests": [ 6 | { 7 | "description": "ECMA 262 has no support for \\Z anchor from .NET", 8 | "data": "^\\S(|(.|\\n)*\\S)\\Z", 9 | "valid": false 10 | } 11 | ] 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /test/fixtures/schemas/optional/zeroTerminatedFloats.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "some languages do not distinguish between different types of numeric value", 4 | "schema": { 5 | "type": "integer" 6 | }, 7 | "tests": [ 8 | { 9 | "description": "a float without fractional part is an integer", 10 | "data": 1.0, 11 | "valid": true 12 | } 13 | ] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /test/fixtures/schemas/pattern.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "pattern validation", 4 | "schema": {"pattern": "^a*$"}, 5 | "tests": [ 6 | { 7 | "description": "a matching pattern is valid", 8 | "data": "aaa", 9 | "valid": true 10 | }, 11 | { 12 | "description": "a non-matching pattern is invalid", 13 | "data": "abc", 14 | "valid": false 15 | }, 16 | { 17 | "description": "ignores non-strings", 18 | "data": true, 19 | "valid": true 20 | } 21 | ] 22 | }, 23 | { 24 | "description": "pattern is not anchored", 25 | "schema": {"pattern": "a+"}, 26 | "tests": [ 27 | { 28 | "description": "matches a substring", 29 | "data": "xxaayy", 30 | "valid": true 31 | } 32 | ] 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /test/fixtures/schemas/patternProperties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": 4 | "patternProperties validates properties matching a regex", 5 | "schema": { 6 | "patternProperties": { 7 | "f.*o": {"type": "integer"} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "a single valid match is valid", 13 | "data": {"foo": 1}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "multiple valid matches is valid", 18 | "data": {"foo": 1, "foooooo" : 2}, 19 | "valid": true 20 | }, 21 | { 22 | "description": "a single invalid match is invalid", 23 | "data": {"foo": "bar", "fooooo": 2}, 24 | "valid": false 25 | }, 26 | { 27 | "description": "multiple invalid matches is invalid", 28 | "data": {"foo": "bar", "foooooo" : "baz"}, 29 | "valid": false 30 | }, 31 | { 32 | "description": "ignores arrays", 33 | "data": ["foo"], 34 | "valid": true 35 | }, 36 | { 37 | "description": "ignores strings", 38 | "data": "foo", 39 | "valid": true 40 | }, 41 | { 42 | "description": "ignores other non-objects", 43 | "data": 12, 44 | "valid": true 45 | } 46 | ] 47 | }, 48 | { 49 | "description": "multiple simultaneous patternProperties are validated", 50 | "schema": { 51 | "patternProperties": { 52 | "a*": {"type": "integer"}, 53 | "aaa*": {"maximum": 20} 54 | } 55 | }, 56 | "tests": [ 57 | { 58 | "description": "a single valid match is valid", 59 | "data": {"a": 21}, 60 | "valid": true 61 | }, 62 | { 63 | "description": "a simultaneous match is valid", 64 | "data": {"aaaa": 18}, 65 | "valid": true 66 | }, 67 | { 68 | "description": "multiple matches is valid", 69 | "data": {"a": 21, "aaaa": 18}, 70 | "valid": true 71 | }, 72 | { 73 | "description": "an invalid due to one is invalid", 74 | "data": {"a": "bar"}, 75 | "valid": false 76 | }, 77 | { 78 | "description": "an invalid due to the other is invalid", 79 | "data": {"aaaa": 31}, 80 | "valid": false 81 | }, 82 | { 83 | "description": "an invalid due to both is invalid", 84 | "data": {"aaa": "foo", "aaaa": 31}, 85 | "valid": false 86 | } 87 | ] 88 | }, 89 | { 90 | "description": "regexes are not anchored by default and are case sensitive", 91 | "schema": { 92 | "patternProperties": { 93 | "[0-9]{2,}": { "type": "boolean" }, 94 | "X_": { "type": "string" } 95 | } 96 | }, 97 | "tests": [ 98 | { 99 | "description": "non recognized members are ignored", 100 | "data": { "answer 1": "42" }, 101 | "valid": true 102 | }, 103 | { 104 | "description": "recognized members are accounted for", 105 | "data": { "a31b": null }, 106 | "valid": false 107 | }, 108 | { 109 | "description": "regexes are case sensitive", 110 | "data": { "a_x_3": 3 }, 111 | "valid": true 112 | }, 113 | { 114 | "description": "regexes are case sensitive, 2", 115 | "data": { "a_X_3": 3 }, 116 | "valid": false 117 | } 118 | ] 119 | }, 120 | { 121 | "description": "patternProperties with boolean schemas", 122 | "schema": { 123 | "patternProperties": { 124 | "f.*": true, 125 | "b.*": false 126 | } 127 | }, 128 | "tests": [ 129 | { 130 | "description": "object with property matching schema true is valid", 131 | "data": {"foo": 1}, 132 | "valid": true 133 | }, 134 | { 135 | "description": "object with property matching schema false is invalid", 136 | "data": {"bar": 2}, 137 | "valid": false 138 | }, 139 | { 140 | "description": "object with both properties is invalid", 141 | "data": {"foo": 1, "bar": 2}, 142 | "valid": false 143 | }, 144 | { 145 | "description": "empty object is valid", 146 | "data": {}, 147 | "valid": true 148 | } 149 | ] 150 | } 151 | ] 152 | -------------------------------------------------------------------------------- /test/fixtures/schemas/properties.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "object properties validation", 4 | "schema": { 5 | "properties": { 6 | "foo": {"type": "integer"}, 7 | "bar": {"type": "string"} 8 | } 9 | }, 10 | "tests": [ 11 | { 12 | "description": "both properties present and valid is valid", 13 | "data": {"foo": 1, "bar": "baz"}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "one property invalid is invalid", 18 | "data": {"foo": 1, "bar": {}}, 19 | "valid": false 20 | }, 21 | { 22 | "description": "both properties invalid is invalid", 23 | "data": {"foo": [], "bar": {}}, 24 | "valid": false 25 | }, 26 | { 27 | "description": "doesn't invalidate other properties", 28 | "data": {"quux": []}, 29 | "valid": true 30 | }, 31 | { 32 | "description": "ignores arrays", 33 | "data": [], 34 | "valid": true 35 | }, 36 | { 37 | "description": "ignores other non-objects", 38 | "data": 12, 39 | "valid": true 40 | } 41 | ] 42 | }, 43 | { 44 | "description": 45 | "properties, patternProperties, additionalProperties interaction", 46 | "schema": { 47 | "properties": { 48 | "foo": {"type": "array", "maxItems": 3}, 49 | "bar": {"type": "array"} 50 | }, 51 | "patternProperties": {"f.o": {"minItems": 2}}, 52 | "additionalProperties": {"type": "integer"} 53 | }, 54 | "tests": [ 55 | { 56 | "description": "property validates property", 57 | "data": {"foo": [1, 2]}, 58 | "valid": true 59 | }, 60 | { 61 | "description": "property invalidates property", 62 | "data": {"foo": [1, 2, 3, 4]}, 63 | "valid": false 64 | }, 65 | { 66 | "description": "patternProperty invalidates property", 67 | "data": {"foo": []}, 68 | "valid": false 69 | }, 70 | { 71 | "description": "patternProperty validates nonproperty", 72 | "data": {"fxo": [1, 2]}, 73 | "valid": true 74 | }, 75 | { 76 | "description": "patternProperty invalidates nonproperty", 77 | "data": {"fxo": []}, 78 | "valid": false 79 | }, 80 | { 81 | "description": "additionalProperty ignores property", 82 | "data": {"bar": []}, 83 | "valid": true 84 | }, 85 | { 86 | "description": "additionalProperty validates others", 87 | "data": {"quux": 3}, 88 | "valid": true 89 | }, 90 | { 91 | "description": "additionalProperty invalidates others", 92 | "data": {"quux": "foo"}, 93 | "valid": false 94 | } 95 | ] 96 | }, 97 | { 98 | "description": "properties with boolean schema", 99 | "schema": { 100 | "properties": { 101 | "foo": true, 102 | "bar": false 103 | } 104 | }, 105 | "tests": [ 106 | { 107 | "description": "no property present is valid", 108 | "data": {}, 109 | "valid": true 110 | }, 111 | { 112 | "description": "only 'true' property present is valid", 113 | "data": {"foo": 1}, 114 | "valid": true 115 | }, 116 | { 117 | "description": "only 'false' property present is invalid", 118 | "data": {"bar": 2}, 119 | "valid": false 120 | }, 121 | { 122 | "description": "both properties present is invalid", 123 | "data": {"foo": 1, "bar": 2}, 124 | "valid": false 125 | } 126 | ] 127 | } 128 | ] 129 | -------------------------------------------------------------------------------- /test/fixtures/schemas/propertyNames.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "propertyNames validation", 4 | "schema": { 5 | "propertyNames": {"maxLength": 3} 6 | }, 7 | "tests": [ 8 | { 9 | "description": "all property names valid", 10 | "data": { 11 | "f": {}, 12 | "foo": {} 13 | }, 14 | "valid": true 15 | }, 16 | { 17 | "description": "some property names invalid", 18 | "data": { 19 | "foo": {}, 20 | "foobar": {} 21 | }, 22 | "valid": false 23 | }, 24 | { 25 | "description": "object without properties is valid", 26 | "data": {}, 27 | "valid": true 28 | }, 29 | { 30 | "description": "ignores arrays", 31 | "data": [1, 2, 3, 4], 32 | "valid": true 33 | }, 34 | { 35 | "description": "ignores strings", 36 | "data": "foobar", 37 | "valid": true 38 | }, 39 | { 40 | "description": "ignores other non-objects", 41 | "data": 12, 42 | "valid": true 43 | } 44 | ] 45 | }, 46 | { 47 | "description": "propertyNames with boolean schema true", 48 | "schema": {"propertyNames": true}, 49 | "tests": [ 50 | { 51 | "description": "object with any properties is valid", 52 | "data": {"foo": 1}, 53 | "valid": true 54 | }, 55 | { 56 | "description": "empty object is valid", 57 | "data": {}, 58 | "valid": true 59 | } 60 | ] 61 | }, 62 | { 63 | "description": "propertyNames with boolean schema false", 64 | "schema": {"propertyNames": false}, 65 | "tests": [ 66 | { 67 | "description": "object with any properties is invalid", 68 | "data": {"foo": 1}, 69 | "valid": false 70 | }, 71 | { 72 | "description": "empty object is valid", 73 | "data": {}, 74 | "valid": true 75 | } 76 | ] 77 | } 78 | ] 79 | -------------------------------------------------------------------------------- /test/fixtures/schemas/ref.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "root pointer ref", 4 | "schema": { 5 | "properties": { 6 | "foo": {"$ref": "#"} 7 | }, 8 | "additionalProperties": false 9 | }, 10 | "tests": [ 11 | { 12 | "description": "match", 13 | "data": {"foo": false}, 14 | "valid": true 15 | }, 16 | { 17 | "description": "recursive match", 18 | "data": {"foo": {"foo": false}}, 19 | "valid": true 20 | }, 21 | { 22 | "description": "mismatch", 23 | "data": {"bar": false}, 24 | "valid": false 25 | }, 26 | { 27 | "description": "recursive mismatch", 28 | "data": {"foo": {"bar": false}}, 29 | "valid": false 30 | } 31 | ] 32 | }, 33 | { 34 | "description": "relative pointer ref to object", 35 | "schema": { 36 | "properties": { 37 | "foo": {"type": "integer"}, 38 | "bar": {"$ref": "#/properties/foo"} 39 | } 40 | }, 41 | "tests": [ 42 | { 43 | "description": "match", 44 | "data": {"bar": 3}, 45 | "valid": true 46 | }, 47 | { 48 | "description": "mismatch", 49 | "data": {"bar": true}, 50 | "valid": false 51 | } 52 | ] 53 | }, 54 | { 55 | "description": "relative pointer ref to array", 56 | "schema": { 57 | "items": [ 58 | {"type": "integer"}, 59 | {"$ref": "#/items/0"} 60 | ] 61 | }, 62 | "tests": [ 63 | { 64 | "description": "match array", 65 | "data": [1, 2], 66 | "valid": true 67 | }, 68 | { 69 | "description": "mismatch array", 70 | "data": [1, "foo"], 71 | "valid": false 72 | } 73 | ] 74 | }, 75 | { 76 | "description": "escaped pointer ref", 77 | "schema": { 78 | "tilda~field": {"type": "integer"}, 79 | "slash/field": {"type": "integer"}, 80 | "percent%field": {"type": "integer"}, 81 | "properties": { 82 | "tilda": {"$ref": "#/tilda~0field"}, 83 | "slash": {"$ref": "#/slash~1field"}, 84 | "percent": {"$ref": "#/percent%25field"} 85 | } 86 | }, 87 | "tests": [ 88 | { 89 | "description": "slash invalid", 90 | "data": {"slash": "aoeu"}, 91 | "valid": false 92 | }, 93 | { 94 | "description": "tilda invalid", 95 | "data": {"tilda": "aoeu"}, 96 | "valid": false 97 | }, 98 | { 99 | "description": "percent invalid", 100 | "data": {"percent": "aoeu"}, 101 | "valid": false 102 | }, 103 | { 104 | "description": "slash valid", 105 | "data": {"slash": 123}, 106 | "valid": true 107 | }, 108 | { 109 | "description": "tilda valid", 110 | "data": {"tilda": 123}, 111 | "valid": true 112 | }, 113 | { 114 | "description": "percent valid", 115 | "data": {"percent": 123}, 116 | "valid": true 117 | } 118 | ] 119 | }, 120 | { 121 | "description": "nested refs", 122 | "schema": { 123 | "definitions": { 124 | "a": {"type": "integer"}, 125 | "b": {"$ref": "#/definitions/a"}, 126 | "c": {"$ref": "#/definitions/b"} 127 | }, 128 | "$ref": "#/definitions/c" 129 | }, 130 | "tests": [ 131 | { 132 | "description": "nested ref valid", 133 | "data": 5, 134 | "valid": true 135 | }, 136 | { 137 | "description": "nested ref invalid", 138 | "data": "a", 139 | "valid": false 140 | } 141 | ] 142 | }, 143 | { 144 | "description": "ref overrides any sibling keywords", 145 | "schema": { 146 | "definitions": { 147 | "reffed": { 148 | "type": "array" 149 | } 150 | }, 151 | "properties": { 152 | "foo": { 153 | "$ref": "#/definitions/reffed", 154 | "maxItems": 2 155 | } 156 | } 157 | }, 158 | "tests": [ 159 | { 160 | "description": "ref valid", 161 | "data": { "foo": [] }, 162 | "valid": true 163 | }, 164 | { 165 | "description": "ref valid, maxItems ignored", 166 | "data": { "foo": [ 1, 2, 3] }, 167 | "valid": true 168 | }, 169 | { 170 | "description": "ref invalid", 171 | "data": { "foo": "string" }, 172 | "valid": false 173 | } 174 | ] 175 | }, 176 | { 177 | "description": "remote ref, containing refs itself", 178 | "schema": {"$ref": "http://json-schema.org/draft-06/schema#"}, 179 | "tests": [ 180 | { 181 | "description": "remote ref valid", 182 | "data": {"minLength": 1}, 183 | "valid": true 184 | }, 185 | { 186 | "description": "remote ref invalid", 187 | "data": {"minLength": -1}, 188 | "valid": false 189 | } 190 | ] 191 | }, 192 | { 193 | "description": "property named $ref that is not a reference", 194 | "schema": { 195 | "properties": { 196 | "$ref": {"type": "string"} 197 | } 198 | }, 199 | "tests": [ 200 | { 201 | "description": "property named $ref valid", 202 | "data": {"$ref": "a"}, 203 | "valid": true 204 | }, 205 | { 206 | "description": "property named $ref invalid", 207 | "data": {"$ref": 2}, 208 | "valid": false 209 | } 210 | ] 211 | }, 212 | { 213 | "description": "$ref to boolean schema true", 214 | "schema": { 215 | "$ref": "#/definitions/bool", 216 | "definitions": { 217 | "bool": true 218 | } 219 | }, 220 | "tests": [ 221 | { 222 | "description": "any value is valid", 223 | "data": "foo", 224 | "valid": true 225 | } 226 | ] 227 | }, 228 | { 229 | "description": "$ref to boolean schema false", 230 | "schema": { 231 | "$ref": "#/definitions/bool", 232 | "definitions": { 233 | "bool": false 234 | } 235 | }, 236 | "tests": [ 237 | { 238 | "description": "any value is invalid", 239 | "data": "foo", 240 | "valid": false 241 | } 242 | ] 243 | }, 244 | { 245 | "description": "Recursive references between schemas", 246 | "schema": { 247 | "$id": "http://localhost:1234/tree", 248 | "description": "tree of nodes", 249 | "type": "object", 250 | "properties": { 251 | "meta": {"type": "string"}, 252 | "nodes": { 253 | "type": "array", 254 | "items": {"$ref": "node"} 255 | } 256 | }, 257 | "required": ["meta", "nodes"], 258 | "definitions": { 259 | "node": { 260 | "$id": "http://localhost:1234/node", 261 | "description": "node", 262 | "type": "object", 263 | "properties": { 264 | "value": {"type": "number"}, 265 | "subtree": {"$ref": "tree"} 266 | }, 267 | "required": ["value"] 268 | } 269 | } 270 | }, 271 | "tests": [ 272 | { 273 | "description": "valid tree", 274 | "data": { 275 | "meta": "root", 276 | "nodes": [ 277 | { 278 | "value": 1, 279 | "subtree": { 280 | "meta": "child", 281 | "nodes": [ 282 | {"value": 1.1}, 283 | {"value": 1.2} 284 | ] 285 | } 286 | }, 287 | { 288 | "value": 2, 289 | "subtree": { 290 | "meta": "child", 291 | "nodes": [ 292 | {"value": 2.1}, 293 | {"value": 2.2} 294 | ] 295 | } 296 | } 297 | ] 298 | }, 299 | "valid": true 300 | }, 301 | { 302 | "description": "invalid tree", 303 | "data": { 304 | "meta": "root", 305 | "nodes": [ 306 | { 307 | "value": 1, 308 | "subtree": { 309 | "meta": "child", 310 | "nodes": [ 311 | {"value": "string is invalid"}, 312 | {"value": 1.2} 313 | ] 314 | } 315 | }, 316 | { 317 | "value": 2, 318 | "subtree": { 319 | "meta": "child", 320 | "nodes": [ 321 | {"value": 2.1}, 322 | {"value": 2.2} 323 | ] 324 | } 325 | } 326 | ] 327 | }, 328 | "valid": false 329 | } 330 | ] 331 | } 332 | ] 333 | -------------------------------------------------------------------------------- /test/fixtures/schemas/refRemote.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "remote ref", 4 | "schema": {"$ref": "http://localhost:1234/integer.json"}, 5 | "tests": [ 6 | { 7 | "description": "remote ref valid", 8 | "data": 1, 9 | "valid": true 10 | }, 11 | { 12 | "description": "remote ref invalid", 13 | "data": "a", 14 | "valid": false 15 | } 16 | ] 17 | }, 18 | { 19 | "description": "fragment within remote ref", 20 | "schema": {"$ref": "http://localhost:1234/subSchemas.json#/integer"}, 21 | "tests": [ 22 | { 23 | "description": "remote fragment valid", 24 | "data": 1, 25 | "valid": true 26 | }, 27 | { 28 | "description": "remote fragment invalid", 29 | "data": "a", 30 | "valid": false 31 | } 32 | ] 33 | }, 34 | { 35 | "description": "ref within remote ref", 36 | "schema": { 37 | "$ref": "http://localhost:1234/subSchemas.json#/refToInteger" 38 | }, 39 | "tests": [ 40 | { 41 | "description": "ref within ref valid", 42 | "data": 1, 43 | "valid": true 44 | }, 45 | { 46 | "description": "ref within ref invalid", 47 | "data": "a", 48 | "valid": false 49 | } 50 | ] 51 | }, 52 | { 53 | "description": "base URI change", 54 | "schema": { 55 | "$id": "http://localhost:1234/", 56 | "items": { 57 | "$id": "folder/", 58 | "items": {"$ref": "folderInteger.json"} 59 | } 60 | }, 61 | "tests": [ 62 | { 63 | "description": "base URI change ref valid", 64 | "data": [[1]], 65 | "valid": true 66 | }, 67 | { 68 | "description": "base URI change ref invalid", 69 | "data": [["a"]], 70 | "valid": false 71 | } 72 | ] 73 | }, 74 | { 75 | "description": "base URI change - change folder", 76 | "schema": { 77 | "$id": "http://localhost:1234/scope_change_defs1.json", 78 | "type" : "object", 79 | "properties": { 80 | "list": {"$ref": "#/definitions/baz"} 81 | }, 82 | "definitions": { 83 | "baz": { 84 | "$id": "folder/", 85 | "type": "array", 86 | "items": {"$ref": "folderInteger.json"} 87 | } 88 | } 89 | }, 90 | "tests": [ 91 | { 92 | "description": "number is valid", 93 | "data": {"list": [1]}, 94 | "valid": true 95 | }, 96 | { 97 | "description": "string is invalid", 98 | "data": {"list": ["a"]}, 99 | "valid": false 100 | } 101 | ] 102 | }, 103 | { 104 | "description": "base URI change - change folder in subschema", 105 | "schema": { 106 | "$id": "http://localhost:1234/scope_change_defs2.json", 107 | "type" : "object", 108 | "properties": { 109 | "list": {"$ref": "#/definitions/baz/definitions/bar"} 110 | }, 111 | "definitions": { 112 | "baz": { 113 | "$id": "folder/", 114 | "definitions": { 115 | "bar": { 116 | "type": "array", 117 | "items": {"$ref": "folderInteger.json"} 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "tests": [ 124 | { 125 | "description": "number is valid", 126 | "data": {"list": [1]}, 127 | "valid": true 128 | }, 129 | { 130 | "description": "string is invalid", 131 | "data": {"list": ["a"]}, 132 | "valid": false 133 | } 134 | ] 135 | }, 136 | { 137 | "description": "root ref in remote ref", 138 | "schema": { 139 | "$id": "http://localhost:1234/object", 140 | "type": "object", 141 | "properties": { 142 | "name": {"$ref": "name.json#/definitions/orNull"} 143 | } 144 | }, 145 | "tests": [ 146 | { 147 | "description": "string is valid", 148 | "data": { 149 | "name": "foo" 150 | }, 151 | "valid": true 152 | }, 153 | { 154 | "description": "null is valid", 155 | "data": { 156 | "name": null 157 | }, 158 | "valid": true 159 | }, 160 | { 161 | "description": "object is invalid", 162 | "data": { 163 | "name": { 164 | "name": null 165 | } 166 | }, 167 | "valid": false 168 | } 169 | ] 170 | } 171 | ] 172 | -------------------------------------------------------------------------------- /test/fixtures/schemas/required.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "required validation", 4 | "schema": { 5 | "properties": { 6 | "foo": {}, 7 | "bar": {} 8 | }, 9 | "required": ["foo"] 10 | }, 11 | "tests": [ 12 | { 13 | "description": "present required property is valid", 14 | "data": {"foo": 1}, 15 | "valid": true 16 | }, 17 | { 18 | "description": "non-present required property is invalid", 19 | "data": {"bar": 1}, 20 | "valid": false 21 | }, 22 | { 23 | "description": "ignores arrays", 24 | "data": [], 25 | "valid": true 26 | }, 27 | { 28 | "description": "ignores strings", 29 | "data": "", 30 | "valid": true 31 | }, 32 | { 33 | "description": "ignores other non-objects", 34 | "data": 12, 35 | "valid": true 36 | } 37 | ] 38 | }, 39 | { 40 | "description": "required default validation", 41 | "schema": { 42 | "properties": { 43 | "foo": {} 44 | } 45 | }, 46 | "tests": [ 47 | { 48 | "description": "not required by default", 49 | "data": {}, 50 | "valid": true 51 | } 52 | ] 53 | }, 54 | { 55 | "description": "required with empty array", 56 | "schema": { 57 | "properties": { 58 | "foo": {} 59 | }, 60 | "required": [] 61 | }, 62 | "tests": [ 63 | { 64 | "description": "property not required", 65 | "data": {}, 66 | "valid": true 67 | } 68 | ] 69 | } 70 | ] 71 | -------------------------------------------------------------------------------- /test/fixtures/schemas/type.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "integer type matches integers", 4 | "schema": {"type": "integer"}, 5 | "tests": [ 6 | { 7 | "description": "an integer is an integer", 8 | "data": 1, 9 | "valid": true 10 | }, 11 | { 12 | "description": "a float is not an integer", 13 | "data": 1.1, 14 | "valid": false 15 | }, 16 | { 17 | "description": "a string is not an integer", 18 | "data": "foo", 19 | "valid": false 20 | }, 21 | { 22 | "description": "a string is still not an integer, even if it looks like one", 23 | "data": "1", 24 | "valid": false 25 | }, 26 | { 27 | "description": "an object is not an integer", 28 | "data": {}, 29 | "valid": false 30 | }, 31 | { 32 | "description": "an array is not an integer", 33 | "data": [], 34 | "valid": false 35 | }, 36 | { 37 | "description": "a boolean is not an integer", 38 | "data": true, 39 | "valid": false 40 | }, 41 | { 42 | "description": "null is not an integer", 43 | "data": null, 44 | "valid": false 45 | } 46 | ] 47 | }, 48 | { 49 | "description": "number type matches numbers", 50 | "schema": {"type": "number"}, 51 | "tests": [ 52 | { 53 | "description": "an integer is a number", 54 | "data": 1, 55 | "valid": true 56 | }, 57 | { 58 | "description": "a float is a number", 59 | "data": 1.1, 60 | "valid": true 61 | }, 62 | { 63 | "description": "a string is not a number", 64 | "data": "foo", 65 | "valid": false 66 | }, 67 | { 68 | "description": "a string is still not a number, even if it looks like one", 69 | "data": "1", 70 | "valid": false 71 | }, 72 | { 73 | "description": "an object is not a number", 74 | "data": {}, 75 | "valid": false 76 | }, 77 | { 78 | "description": "an array is not a number", 79 | "data": [], 80 | "valid": false 81 | }, 82 | { 83 | "description": "a boolean is not a number", 84 | "data": true, 85 | "valid": false 86 | }, 87 | { 88 | "description": "null is not a number", 89 | "data": null, 90 | "valid": false 91 | } 92 | ] 93 | }, 94 | { 95 | "description": "string type matches strings", 96 | "schema": {"type": "string"}, 97 | "tests": [ 98 | { 99 | "description": "1 is not a string", 100 | "data": 1, 101 | "valid": false 102 | }, 103 | { 104 | "description": "a float is not a string", 105 | "data": 1.1, 106 | "valid": false 107 | }, 108 | { 109 | "description": "a string is a string", 110 | "data": "foo", 111 | "valid": true 112 | }, 113 | { 114 | "description": "a string is still a string, even if it looks like a number", 115 | "data": "1", 116 | "valid": true 117 | }, 118 | { 119 | "description": "an object is not a string", 120 | "data": {}, 121 | "valid": false 122 | }, 123 | { 124 | "description": "an array is not a string", 125 | "data": [], 126 | "valid": false 127 | }, 128 | { 129 | "description": "a boolean is not a string", 130 | "data": true, 131 | "valid": false 132 | }, 133 | { 134 | "description": "null is not a string", 135 | "data": null, 136 | "valid": false 137 | } 138 | ] 139 | }, 140 | { 141 | "description": "object type matches objects", 142 | "schema": {"type": "object"}, 143 | "tests": [ 144 | { 145 | "description": "an integer is not an object", 146 | "data": 1, 147 | "valid": false 148 | }, 149 | { 150 | "description": "a float is not an object", 151 | "data": 1.1, 152 | "valid": false 153 | }, 154 | { 155 | "description": "a string is not an object", 156 | "data": "foo", 157 | "valid": false 158 | }, 159 | { 160 | "description": "an object is an object", 161 | "data": {}, 162 | "valid": true 163 | }, 164 | { 165 | "description": "an array is not an object", 166 | "data": [], 167 | "valid": false 168 | }, 169 | { 170 | "description": "a boolean is not an object", 171 | "data": true, 172 | "valid": false 173 | }, 174 | { 175 | "description": "null is not an object", 176 | "data": null, 177 | "valid": false 178 | } 179 | ] 180 | }, 181 | { 182 | "description": "array type matches arrays", 183 | "schema": {"type": "array"}, 184 | "tests": [ 185 | { 186 | "description": "an integer is not an array", 187 | "data": 1, 188 | "valid": false 189 | }, 190 | { 191 | "description": "a float is not an array", 192 | "data": 1.1, 193 | "valid": false 194 | }, 195 | { 196 | "description": "a string is not an array", 197 | "data": "foo", 198 | "valid": false 199 | }, 200 | { 201 | "description": "an object is not an array", 202 | "data": {}, 203 | "valid": false 204 | }, 205 | { 206 | "description": "an array is an array", 207 | "data": [], 208 | "valid": true 209 | }, 210 | { 211 | "description": "a boolean is not an array", 212 | "data": true, 213 | "valid": false 214 | }, 215 | { 216 | "description": "null is not an array", 217 | "data": null, 218 | "valid": false 219 | } 220 | ] 221 | }, 222 | { 223 | "description": "boolean type matches booleans", 224 | "schema": {"type": "boolean"}, 225 | "tests": [ 226 | { 227 | "description": "an integer is not a boolean", 228 | "data": 1, 229 | "valid": false 230 | }, 231 | { 232 | "description": "a float is not a boolean", 233 | "data": 1.1, 234 | "valid": false 235 | }, 236 | { 237 | "description": "a string is not a boolean", 238 | "data": "foo", 239 | "valid": false 240 | }, 241 | { 242 | "description": "an object is not a boolean", 243 | "data": {}, 244 | "valid": false 245 | }, 246 | { 247 | "description": "an array is not a boolean", 248 | "data": [], 249 | "valid": false 250 | }, 251 | { 252 | "description": "a boolean is a boolean", 253 | "data": true, 254 | "valid": true 255 | }, 256 | { 257 | "description": "null is not a boolean", 258 | "data": null, 259 | "valid": false 260 | } 261 | ] 262 | }, 263 | { 264 | "description": "null type matches only the null object", 265 | "schema": {"type": "null"}, 266 | "tests": [ 267 | { 268 | "description": "an integer is not null", 269 | "data": 1, 270 | "valid": false 271 | }, 272 | { 273 | "description": "a float is not null", 274 | "data": 1.1, 275 | "valid": false 276 | }, 277 | { 278 | "description": "a string is not null", 279 | "data": "foo", 280 | "valid": false 281 | }, 282 | { 283 | "description": "an object is not null", 284 | "data": {}, 285 | "valid": false 286 | }, 287 | { 288 | "description": "an array is not null", 289 | "data": [], 290 | "valid": false 291 | }, 292 | { 293 | "description": "a boolean is not null", 294 | "data": true, 295 | "valid": false 296 | }, 297 | { 298 | "description": "null is null", 299 | "data": null, 300 | "valid": true 301 | } 302 | ] 303 | }, 304 | { 305 | "description": "multiple types can be specified in an array", 306 | "schema": {"type": ["integer", "string"]}, 307 | "tests": [ 308 | { 309 | "description": "an integer is valid", 310 | "data": 1, 311 | "valid": true 312 | }, 313 | { 314 | "description": "a string is valid", 315 | "data": "foo", 316 | "valid": true 317 | }, 318 | { 319 | "description": "a float is invalid", 320 | "data": 1.1, 321 | "valid": false 322 | }, 323 | { 324 | "description": "an object is invalid", 325 | "data": {}, 326 | "valid": false 327 | }, 328 | { 329 | "description": "an array is invalid", 330 | "data": [], 331 | "valid": false 332 | }, 333 | { 334 | "description": "a boolean is invalid", 335 | "data": true, 336 | "valid": false 337 | }, 338 | { 339 | "description": "null is invalid", 340 | "data": null, 341 | "valid": false 342 | } 343 | ] 344 | } 345 | ] 346 | -------------------------------------------------------------------------------- /test/fixtures/schemas/uniqueItems.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "uniqueItems validation", 4 | "schema": {"uniqueItems": true}, 5 | "tests": [ 6 | { 7 | "description": "unique array of integers is valid", 8 | "data": [1, 2], 9 | "valid": true 10 | }, 11 | { 12 | "description": "non-unique array of integers is invalid", 13 | "data": [1, 1], 14 | "valid": false 15 | }, 16 | { 17 | "description": "numbers are unique if mathematically unequal", 18 | "data": [1.0, 1.00, 1], 19 | "valid": false 20 | }, 21 | { 22 | "description": "unique array of objects is valid", 23 | "data": [{"foo": "bar"}, {"foo": "baz"}], 24 | "valid": true 25 | }, 26 | { 27 | "description": "non-unique array of objects is invalid", 28 | "data": [{"foo": "bar"}, {"foo": "bar"}], 29 | "valid": false 30 | }, 31 | { 32 | "description": "unique array of nested objects is valid", 33 | "data": [ 34 | {"foo": {"bar" : {"baz" : true}}}, 35 | {"foo": {"bar" : {"baz" : false}}} 36 | ], 37 | "valid": true 38 | }, 39 | { 40 | "description": "non-unique array of nested objects is invalid", 41 | "data": [ 42 | {"foo": {"bar" : {"baz" : true}}}, 43 | {"foo": {"bar" : {"baz" : true}}} 44 | ], 45 | "valid": false 46 | }, 47 | { 48 | "description": "unique array of arrays is valid", 49 | "data": [["foo"], ["bar"]], 50 | "valid": true 51 | }, 52 | { 53 | "description": "non-unique array of arrays is invalid", 54 | "data": [["foo"], ["foo"]], 55 | "valid": false 56 | }, 57 | { 58 | "description": "1 and true are unique", 59 | "data": [1, true], 60 | "valid": true 61 | }, 62 | { 63 | "description": "0 and false are unique", 64 | "data": [0, false], 65 | "valid": true 66 | }, 67 | { 68 | "description": "unique heterogeneous types are valid", 69 | "data": [{}, [1], true, null, 1], 70 | "valid": true 71 | }, 72 | { 73 | "description": "non-unique heterogeneous types are invalid", 74 | "data": [{}, [1], true, null, {}, 1], 75 | "valid": false 76 | } 77 | ] 78 | } 79 | ] 80 | -------------------------------------------------------------------------------- /test/specs/merge-validation-options.spec.js: -------------------------------------------------------------------------------- 1 | var mergeStrategy = require('../../src/merge-validation-options') 2 | var chai = require('chai') 3 | var expect = chai.expect 4 | 5 | describe('merge-validation-options', function() { 6 | it('merges extra properties', function() { 7 | var result = mergeStrategy({ 8 | conflict: 1, 9 | orig: 5 10 | }, { 11 | conflict: 2, 12 | extra: 7 13 | }) 14 | 15 | expect(result).to.eql({ 16 | conflict: 2, 17 | orig: 5, 18 | extra: 7 19 | }) 20 | }) 21 | 22 | it('merges plain objects', function() { 23 | var result = mergeStrategy({ 24 | foo: 1 25 | }, { 26 | foo: 2 27 | }) 28 | 29 | expect(result).to.eql({ 30 | foo: 2 31 | }) 32 | }) 33 | 34 | it('merges function and object', function() { 35 | var result = mergeStrategy(function() { 36 | return { 37 | foo: 1 38 | } 39 | }, { 40 | foo: 2 41 | }) 42 | 43 | expect(result).to.be.a('function') 44 | expect(result()).to.eql({ 45 | foo: 2 46 | }) 47 | }) 48 | 49 | it('merges object and function', function() { 50 | var result = mergeStrategy({ 51 | foo: 2 52 | }, function() { 53 | return { 54 | foo: 1 55 | } 56 | }) 57 | 58 | expect(result).to.be.a('function') 59 | expect(result()).to.eql({ 60 | foo: 1 61 | }) 62 | }) 63 | 64 | it('merges function and function', function() { 65 | var result = mergeStrategy(function() { 66 | return { 67 | foo: 1 68 | } 69 | }, function() { 70 | return { 71 | foo: 3 72 | } 73 | }) 74 | 75 | expect(result).to.be.a('function') 76 | expect(result()).to.eql({ 77 | foo: 3 78 | }) 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /test/specs/scaffold.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var scaffold = requireUncached('../../src/scaffold') 4 | 5 | var expect = chai.expect 6 | 7 | describe('scaffold', function() { 8 | it('does not deep scaffold anyOf keyword or use default value', function() { 9 | var result = scaffold([{ 10 | mountPoint: '.', 11 | schema: { 12 | anyOf: [{ 13 | type: 'object', 14 | properties: { 15 | name: { 16 | type: 'string' 17 | }, 18 | sub: { 19 | type: 'object', 20 | properties: { 21 | deep: { 22 | type: 'string' 23 | } 24 | } 25 | } 26 | }, 27 | required: ['name'] 28 | }] 29 | } 30 | }]) 31 | 32 | expect(result).to.eql({ 33 | name: undefined, 34 | sub: undefined 35 | }) 36 | }) 37 | 38 | it('does not deep scaffold not keyword or use default value', function() { 39 | var result = scaffold([{ 40 | mountPoint: '.', 41 | schema: { 42 | not: { 43 | type: 'object', 44 | properties: { 45 | name: { 46 | type: 'string' 47 | }, 48 | sub: { 49 | type: 'object', 50 | properties: { 51 | deep: { 52 | type: 'string' 53 | } 54 | } 55 | } 56 | }, 57 | required: ['name'] 58 | } 59 | } 60 | }]) 61 | 62 | expect(result).to.eql({ 63 | name: undefined, 64 | sub: undefined 65 | }) 66 | }) 67 | 68 | it('does not deep scaffold oneOf keyword or use default value', function() { 69 | var result = scaffold([{ 70 | mountPoint: '.', 71 | schema: { 72 | oneOf: [{ 73 | type: 'object', 74 | properties: { 75 | name: { 76 | type: 'string' 77 | }, 78 | sub: { 79 | type: 'object', 80 | properties: { 81 | deep: { 82 | type: 'string' 83 | } 84 | } 85 | } 86 | }, 87 | required: ['name'] 88 | }] 89 | } 90 | }]) 91 | 92 | expect(result).to.eql({ 93 | name: undefined, 94 | sub: undefined 95 | }) 96 | }) 97 | }) 98 | -------------------------------------------------------------------------------- /test/specs/schemas.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var plugin = requireUncached('../../src') 4 | var Vue = requireUncached('vue') 5 | var fs = require('fs') 6 | var _ = require('lodash') 7 | var path = require('path') 8 | var Vuelidate = requireUncached('vuelidate') 9 | var glob = require('glob') 10 | var $RefParser = require('json-schema-ref-parser') 11 | var expect = chai.expect 12 | 13 | // TODO: when this list is empty I think we are pretty much feature complete 14 | // some of these we have to remove though, as they are invalid, and testing meta functionality 15 | var todoList = [ 16 | // results in a circular schema when dereferenced, that is not supported yet 17 | 'definitions.json', 18 | // does not support internal refs yet 19 | 'ref.json', 20 | // will not support this testcase, as it has dependencies on a localhost etc. 21 | 'refRemote.json' 22 | ] 23 | 24 | // set this to some part of filename to only run one file 25 | var onlyRun = '' 26 | 27 | var schemas = glob.sync(path.join(__dirname, '../fixtures/schemas/**.json')).filter(function(file) { 28 | if (onlyRun.length) { 29 | return file.indexOf(onlyRun) !== -1 30 | } else { 31 | return todoList.every(function(exclude) { 32 | return file.indexOf(exclude) === -1 33 | }) 34 | } 35 | }).map(function(file) { 36 | return JSON.parse(fs.readFileSync(file, 'utf-8')) 37 | }) 38 | 39 | describe('schema fixtures validation', function() { 40 | before(function() { 41 | return Promise.all(schemas.map(function(innerSchema) { 42 | return Promise.all(innerSchema.map(function(config) { 43 | // TODO support boolean schemas 44 | if (_.isPlainObject(config.schema)) { 45 | return $RefParser.dereference(config.schema).then(function(dereferenced) { 46 | innerSchema.schema = dereferenced 47 | return innerSchema 48 | }) 49 | } else { 50 | innerSchema.schema = config.schema 51 | return innerSchema 52 | } 53 | })) 54 | })).then(function(all) { 55 | var hasRefStill = JSON.stringify(all.map(function(obj) { 56 | return obj.schema 57 | })).indexOf('$ref') !== -1 58 | 59 | if (hasRefStill) { 60 | throw new Error('Error in test precondition, some refs are not resolved') 61 | } 62 | 63 | schemas = all 64 | }) 65 | }) 66 | 67 | beforeEach(function() { 68 | Vue.use(plugin) 69 | Vue.use(Vuelidate.Vuelidate) 70 | }) 71 | 72 | schemas.forEach(function(schema) { 73 | schema.forEach(function(innerSchema) { 74 | describe(innerSchema.description, function() { 75 | var vm 76 | beforeEach(function() { 77 | vm = new Vue({ 78 | schema: { 79 | mountPoint: 'mountPoint', 80 | schema: innerSchema.schema 81 | } 82 | }) 83 | }) 84 | 85 | innerSchema.tests.forEach(function(test) { 86 | describe(test.description, function() { 87 | it('is valid according to fixture data', function() { 88 | vm.mountPoint = test.data 89 | var isValid = !vm.$v.mountPoint.$invalid 90 | if (isValid !== test.valid && true) { 91 | console.log(JSON.stringify(vm.$v.mountPoint, null, 2)) 92 | } 93 | expect(isValid).to.eql(test.valid) 94 | }) 95 | 96 | it('gets data', function() { 97 | vm.mountPoint = test.data 98 | // only test data export if the object is expected to be valid 99 | // TODO: I think this is correct, but need to look into it 100 | if (test.valid === true && vm.$v.mountPoint.$invalid === !test.valid) { 101 | expect(removeUndefined(vm.getSchemaData(vm.$schema[0]))).to.eql(test.data) 102 | } 103 | }) 104 | }) 105 | }) 106 | }) 107 | }) 108 | }) 109 | }) 110 | 111 | function removeUndefined(obj) { 112 | return JSON.parse(JSON.stringify(obj)) 113 | } 114 | -------------------------------------------------------------------------------- /test/specs/validators/additionalItems.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var additionalItemsValidator = requireUncached('../../../src/validators/additionalItems') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe('additionalItemsValidator', function() { 9 | describe('when simple', function() { 10 | var schema = { 11 | type: 'array', 12 | items: [true], 13 | additionalItems: { 14 | anyOf: [ 15 | { 16 | type: 'number' 17 | }, { 18 | type: 'boolean' 19 | } 20 | ] 21 | } 22 | } 23 | beforeEach(function() { 24 | validator = additionalItemsValidator(schema, schema.additionalItems, propertyRules.getPropertyValidationRules) 25 | }) 26 | 27 | it('only validates additional items', function() { 28 | expect(validator([1])).to.eql(true) 29 | }) 30 | 31 | it('validates additional items', function() { 32 | expect(validator([1, 'test'])).to.eql(false) 33 | expect(validator([1, {}])).to.eql(false) 34 | expect(validator([1, 1])).to.eql(true) 35 | expect(validator([1, false])).to.eql(true) 36 | }) 37 | }) 38 | 39 | describe('when nested', function() { 40 | var schema = { 41 | type: 'array', 42 | items: [true], 43 | additionalItems: { 44 | anyOf: [ 45 | { 46 | type: 'object', 47 | properties: { 48 | children: { 49 | type: 'array', 50 | items: { 51 | type: 'number', 52 | minimum: 5 53 | } 54 | } 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | beforeEach(function() { 61 | validator = additionalItemsValidator(schema, schema.additionalItems, propertyRules.getPropertyValidationRules) 62 | }) 63 | 64 | it('validates nested schemas', function() { 65 | expect(validator([1, {}])).to.eql(true) 66 | 67 | expect(validator([1, { 68 | children: null 69 | }])).to.eql(false) 70 | 71 | expect(validator([1, { 72 | children: [] 73 | }])).to.eql(true) 74 | 75 | expect(validator([1, { 76 | children: [4] 77 | }])).to.eql(false) 78 | 79 | expect(validator([1, { 80 | children: [5] 81 | }])).to.eql(true) 82 | }) 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /test/specs/validators/allOf.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var allOfValidator = requireUncached('../../../src/validators/allOf') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe.skip('allOfValidator', function() { 9 | describe('when string', function() { 10 | var schema = { 11 | type: 'string', 12 | allOf: [ 13 | { 14 | type: 'string', 15 | minLength: 5 16 | }, { 17 | type: 'string', 18 | pattern: '\\d{2,}' 19 | }, { 20 | type: 'string', 21 | pattern: '[a-z]{2,}' 22 | } 23 | ] 24 | } 25 | beforeEach(function() { 26 | validator = allOfValidator(schema, schema.allOf, propertyRules.getPropertyValidationRules) 27 | }) 28 | 29 | it('is valid if undefined, required validator handles that', function() { 30 | expect(validator(undefined)).to.eql(true) 31 | }) 32 | 33 | it('is valid if not of type string, type validator handles that', function() { 34 | expect(validator(null)).to.eql(true) 35 | expect(validator(1)).to.eql(true) 36 | expect(validator(1.5)).to.eql(true) 37 | expect(validator(false)).to.eql(true) 38 | expect(validator({})).to.eql(true) 39 | expect(validator([])).to.eql(true) 40 | }) 41 | 42 | it('is not valid if to short', function() { 43 | expect(validator('')).to.eql(false) 44 | expect(validator('12ab')).to.eql(false) 45 | expect(validator('12aba')).to.eql(true) 46 | }) 47 | 48 | it('is not valid if missing 2 digits', function() { 49 | expect(validator('1adab')).to.eql(false) 50 | expect(validator('11aba')).to.eql(true) 51 | }) 52 | 53 | it('is not valid if missing 2 letters', function() { 54 | expect(validator('1234a')).to.eql(false) 55 | expect(validator('123ab')).to.eql(true) 56 | }) 57 | }) 58 | 59 | describe('when object', function() { 60 | var schema = { 61 | type: 'object', 62 | allOf: [ 63 | { 64 | type: 'object', 65 | properties: { 66 | name: { 67 | type: 'string', 68 | maxLength: 3 69 | } 70 | } 71 | }, { 72 | type: 'object', 73 | properties: { 74 | name: { 75 | type: 'string' 76 | }, 77 | sub: { 78 | type: 'object', 79 | properties: { 80 | subsub: { 81 | type: 'object', 82 | properties: { 83 | subsub: { 84 | type: 'object', 85 | properties: { 86 | name: { 87 | type: 'string', 88 | minLength: 3 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | }, 97 | required: ['name'] 98 | } 99 | ] 100 | } 101 | beforeEach(function() { 102 | validator = allOfValidator(schema, schema.allOf, propertyRules.getPropertyValidationRules) 103 | }) 104 | 105 | it('is valid when undefined or not object, other validators handle that', function() { 106 | expect(validator(undefined)).to.eql(true) 107 | expect(validator(null)).to.eql(true) 108 | expect(validator(1)).to.eql(true) 109 | expect(validator(1.5)).to.eql(true) 110 | expect(validator(false)).to.eql(true) 111 | expect(validator('')).to.eql(true) 112 | expect(validator([])).to.eql(true) 113 | }) 114 | 115 | it('is invalid if name is missing', function() { 116 | expect(validator({})).to.eql(false) 117 | }) 118 | 119 | it('is invalid if name is too long', function() { 120 | expect(validator({name: 'abcd'})).to.eql(false) 121 | }) 122 | 123 | it('is valid if name is not too long', function() { 124 | expect(validator({name: 'abc'})).to.eql(true) 125 | }) 126 | 127 | it('is invalid if deep property is too short', function() { 128 | expect(validator({ 129 | name: 'abc', 130 | sub: { 131 | subsub: { 132 | subsub: { 133 | name: '12' 134 | } 135 | } 136 | } 137 | })).to.eql(false) 138 | }) 139 | 140 | it('is valid if deep property is missing', function() { 141 | expect(validator({ 142 | name: 'abc', 143 | sub: { 144 | subsub: { 145 | subsub: {} 146 | } 147 | } 148 | })).to.eql(true) 149 | }) 150 | 151 | it('is valid if deep property is long enough', function() { 152 | expect(validator({ 153 | name: 'abc', 154 | sub: { 155 | subsub: { 156 | subsub: { 157 | name: '123' 158 | } 159 | } 160 | } 161 | })).to.eql(true) 162 | }) 163 | }) 164 | }) 165 | -------------------------------------------------------------------------------- /test/specs/validators/anyOf.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var anyOfValidator = requireUncached('../../../src/validators/anyOf') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe('anyOfValidator', function() { 9 | describe('when string', function() { 10 | var schema = { 11 | type: 'string', 12 | anyOf: [ 13 | { 14 | type: 'string', 15 | minLength: 5 16 | }, { 17 | type: 'string', 18 | pattern: '\\d{2,}' 19 | }, { 20 | type: 'string', 21 | pattern: '[a-z]{2,}' 22 | } 23 | ] 24 | } 25 | beforeEach(function() { 26 | validator = anyOfValidator(schema, schema.anyOf, propertyRules.getPropertyValidationRules) 27 | }) 28 | 29 | it('is valid if undefined, required validator handles that', function() { 30 | expect(validator(undefined)).to.eql(true) 31 | }) 32 | 33 | it('is valid if not of type string, type validator handles that', function() { 34 | expect(validator(null)).to.eql(true) 35 | expect(validator(1)).to.eql(true) 36 | expect(validator(1.5)).to.eql(true) 37 | expect(validator(false)).to.eql(true) 38 | expect(validator({})).to.eql(true) 39 | expect(validator([])).to.eql(true) 40 | }) 41 | 42 | it('is invalid if none matches', function() { 43 | expect(validator('--a1')).to.eql(false) 44 | }) 45 | 46 | it('is valid if minLength matches', function() { 47 | expect(validator('---------')).to.eql(true) 48 | }) 49 | 50 | it('is valid if digits', function() { 51 | expect(validator('12')).to.eql(true) 52 | }) 53 | 54 | it('is valid if letters', function() { 55 | expect(validator('ab')).to.eql(true) 56 | }) 57 | 58 | it('is valid if 2 match', function() { 59 | expect(validator('ab12')).to.eql(true) 60 | }) 61 | }) 62 | 63 | describe('when object', function() { 64 | var schema = { 65 | type: 'object', 66 | anyOf: [ 67 | { 68 | type: 'object', 69 | properties: { 70 | name: { 71 | type: 'string', 72 | maxLength: 3 73 | } 74 | } 75 | }, { 76 | type: 'object', 77 | properties: { 78 | name: { 79 | type: 'string', 80 | minLength: 5 81 | }, 82 | sub: { 83 | type: 'object' 84 | } 85 | }, 86 | required: ['name', 'sub'] 87 | } 88 | ] 89 | } 90 | beforeEach(function() { 91 | validator = anyOfValidator(schema, schema.anyOf, propertyRules.getPropertyValidationRules) 92 | }) 93 | 94 | it('is valid when undefined or not object, other validators handle that', function() { 95 | expect(validator(undefined)).to.eql(true) 96 | expect(validator(null)).to.eql(true) 97 | expect(validator(1)).to.eql(true) 98 | expect(validator(1.5)).to.eql(true) 99 | expect(validator(false)).to.eql(true) 100 | expect(validator('')).to.eql(true) 101 | expect(validator([])).to.eql(true) 102 | }) 103 | 104 | it('is invalid if name is 4', function() { 105 | expect(validator({name: 'abcd'})).to.eql(false) 106 | }) 107 | 108 | it('is valid if name is not too long or too short', function() { 109 | expect(validator({name: 'abc'})).to.eql(true) 110 | expect(validator({name: 'abcdd', sub: {}})).to.eql(true) 111 | }) 112 | 113 | it('is valid if sub property is missing, since other definition don\'t require the sub object', function() { 114 | expect(validator({ 115 | name: 'abc' 116 | })).to.eql(true) 117 | }) 118 | }) 119 | }) 120 | -------------------------------------------------------------------------------- /test/specs/validators/items.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var itemsValidator = requireUncached('../../../src/validators/items') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe('itemsValidator', function() { 9 | describe('when items is an array', function() { 10 | var schema = { 11 | type: 'array', 12 | items: [{ 13 | type: 'string', 14 | minLength: 7 15 | }, { 16 | type: 'integer' 17 | }, { 18 | type: 'object', 19 | properties: { 20 | name: { 21 | type: 'string' 22 | }, 23 | childItems: { 24 | type: 'array', 25 | items: [{ 26 | type: 'number', 27 | minimum: 100 28 | }, { 29 | type: 'string' 30 | }] 31 | } 32 | }, 33 | required: ['name'] 34 | }] 35 | } 36 | 37 | beforeEach(function() { 38 | validator = itemsValidator(schema, propertyRules.getPropertyValidationRules) 39 | }) 40 | 41 | it('is valid if each item is valid against the schema at the given position', function() { 42 | // wrong position of number and string 43 | expect(validator([1, 'fdsafdsfdsf', { 44 | name: 'fdsafds' 45 | }])).to.eql(false) 46 | 47 | // correct positions but string is invalid 48 | expect(validator(['fdsa', 3, { 49 | name: 'fdsafds' 50 | }])).to.eql(false) 51 | 52 | // correct positions but object is missing name 53 | expect(validator(['fdsafdsafsd', 3, { 54 | name: undefined 55 | }])).to.eql(false) 56 | 57 | // all ok 58 | expect(validator(['fdsafdsafsd', 3, { 59 | name: 'fdasfds' 60 | }])).to.eql(true) 61 | }) 62 | 63 | it('is invalid if childItems is not correct', function() { 64 | expect(validator(['fdsafdsfdsf', 1, { 65 | name: 'fdsafds', 66 | childItems: [] 67 | }])).to.eql(true) 68 | 69 | expect(validator(['fdsafdsfdsf', 1, { 70 | name: 'fdsafds', 71 | childItems: ['fds', 111] 72 | }])).to.eql(false) 73 | 74 | expect(validator(['fdsafdsfdsf', 1, { 75 | name: 'fdsafds', 76 | childItems: [99, 'fds'] 77 | }])).to.eql(false) 78 | 79 | expect(validator(['fdsafdsfdsf', 1, { 80 | name: 'fdsafds', 81 | childItems: [100, 'fds'] 82 | }])).to.eql(true) 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /test/specs/validators/multipleOf.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var multipleOfValidator = requireUncached('../../../src/validators/multipleOf') 4 | var expect = chai.expect 5 | 6 | var validator 7 | describe('multipleOfValidator', function() { 8 | var schema = { 9 | type: 'number', 10 | multipleOf: 10 11 | } 12 | 13 | it('is valid if multiple of 10', function() { 14 | validator = multipleOfValidator(schema, 10) 15 | expect(validator(0)).to.eql(true) 16 | expect(validator(10)).to.eql(true) 17 | expect(validator(11)).to.eql(false) 18 | }) 19 | 20 | it('is valid if multiple of 0.5', function() { 21 | validator = multipleOfValidator(schema, 0.5) 22 | expect(validator(1)).to.eql(true) 23 | expect(validator(0.9)).to.eql(false) 24 | expect(validator(0.5)).to.eql(true) 25 | expect(validator(0)).to.eql(true) 26 | expect(validator(2)).to.eql(true) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/specs/validators/not.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var notValidator = requireUncached('../../../src/validators/not') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe('notValidator', function() { 9 | describe('when string', function() { 10 | var schema = { 11 | type: 'string', 12 | not: { 13 | type: 'string', 14 | minLength: 5 15 | } 16 | } 17 | beforeEach(function() { 18 | validator = notValidator(schema, schema.not, propertyRules.getPropertyValidationRules) 19 | }) 20 | 21 | it('is valid if undefined, required validator handles that', function() { 22 | expect(validator(undefined)).to.eql(true) 23 | }) 24 | 25 | it('is valid if not of type string, type validator handles that', function() { 26 | expect(validator(null)).to.eql(true) 27 | expect(validator(1)).to.eql(true) 28 | expect(validator(1.5)).to.eql(true) 29 | expect(validator(false)).to.eql(true) 30 | expect(validator({})).to.eql(true) 31 | expect(validator([])).to.eql(true) 32 | }) 33 | 34 | it('is valid if not schema does not match', function() { 35 | expect(validator('1234')).to.eql(true) 36 | }) 37 | 38 | it('is valid if not schema does match', function() { 39 | expect(validator('12345')).to.eql(false) 40 | }) 41 | }) 42 | 43 | describe('when object', function() { 44 | var schema = { 45 | type: 'object', 46 | not: { 47 | type: 'object', 48 | properties: { 49 | name: { 50 | type: 'string', 51 | maxLength: 3 52 | } 53 | } 54 | } 55 | } 56 | beforeEach(function() { 57 | validator = notValidator(schema, schema.not, propertyRules.getPropertyValidationRules) 58 | }) 59 | 60 | it('is valid when undefined or not object, other validators handle that', function() { 61 | expect(validator(undefined)).to.eql(true) 62 | expect(validator(null)).to.eql(true) 63 | expect(validator(1)).to.eql(true) 64 | expect(validator(1.5)).to.eql(true) 65 | expect(validator(false)).to.eql(true) 66 | expect(validator('')).to.eql(true) 67 | expect(validator([])).to.eql(true) 68 | }) 69 | 70 | it('is valid if name is 4', function() { 71 | expect(validator({name: 'abcd'})).to.eql(true) 72 | }) 73 | 74 | it('is invalid if name is 3', function() { 75 | expect(validator({name: 'abc'})).to.eql(false) 76 | }) 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /test/specs/validators/oneOf.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var oneOfValidator = requireUncached('../../../src/validators/oneOf') 4 | var propertyRules = require('../../../src/property-rules') 5 | var expect = chai.expect 6 | 7 | var validator 8 | describe('oneOfValidator', function() { 9 | describe('when string', function() { 10 | var schema = { 11 | type: 'string', 12 | oneOf: [ 13 | { 14 | type: 'string', 15 | minLength: 5 16 | }, { 17 | type: 'string', 18 | pattern: '\\d{2,}' 19 | }, { 20 | type: 'string', 21 | pattern: '[a-z]{2,}' 22 | } 23 | ] 24 | } 25 | beforeEach(function() { 26 | validator = oneOfValidator(schema, schema.oneOf, propertyRules.getPropertyValidationRules) 27 | }) 28 | 29 | it('is valid if undefined, required validator handles that', function() { 30 | expect(validator(undefined)).to.eql(true) 31 | }) 32 | 33 | it('is valid if not of type string, type validator handles that', function() { 34 | expect(validator(null)).to.eql(true) 35 | expect(validator(1)).to.eql(true) 36 | expect(validator(1.5)).to.eql(true) 37 | expect(validator(false)).to.eql(true) 38 | expect(validator({})).to.eql(true) 39 | expect(validator([])).to.eql(true) 40 | }) 41 | 42 | it('is invalid if none matches', function() { 43 | expect(validator('--a1')).to.eql(false) 44 | }) 45 | 46 | it('is valid if minLength matches', function() { 47 | expect(validator('---------')).to.eql(true) 48 | }) 49 | 50 | it('is valid if digits', function() { 51 | expect(validator('12')).to.eql(true) 52 | }) 53 | 54 | it('is valid if letters', function() { 55 | expect(validator('ab')).to.eql(true) 56 | }) 57 | 58 | it('is invalid if 2 match', function() { 59 | expect(validator('ab12')).to.eql(false) 60 | }) 61 | }) 62 | 63 | describe('when object', function() { 64 | var schema = { 65 | type: 'object', 66 | oneOf: [ 67 | { 68 | type: 'object', 69 | properties: { 70 | name: { 71 | type: 'string', 72 | maxLength: 3 73 | } 74 | } 75 | }, { 76 | type: 'object', 77 | properties: { 78 | name: { 79 | type: 'string', 80 | minLength: 5 81 | }, 82 | sub: { 83 | type: 'object' 84 | } 85 | }, 86 | required: ['name', 'sub'] 87 | } 88 | ] 89 | } 90 | beforeEach(function() { 91 | validator = oneOfValidator(schema, schema.oneOf, propertyRules.getPropertyValidationRules) 92 | }) 93 | 94 | it('is valid when undefined or not object, other validators handle that', function() { 95 | expect(validator(undefined)).to.eql(true) 96 | expect(validator(null)).to.eql(true) 97 | expect(validator(1)).to.eql(true) 98 | expect(validator(1.5)).to.eql(true) 99 | expect(validator(false)).to.eql(true) 100 | expect(validator('')).to.eql(true) 101 | expect(validator([])).to.eql(true) 102 | }) 103 | 104 | it('is invalid if name is 4', function() { 105 | expect(validator({name: 'abcd'})).to.eql(false) 106 | }) 107 | 108 | it('is valid if name is not too long or too short', function() { 109 | expect(validator({name: 'abc'})).to.eql(true) 110 | expect(validator({name: 'abcdd', sub: {}})).to.eql(true) 111 | }) 112 | 113 | it('is valid if sub property is missing, since other definition don\'t require the sub object', function() { 114 | expect(validator({ 115 | name: 'abc' 116 | })).to.eql(true) 117 | }) 118 | }) 119 | }) 120 | -------------------------------------------------------------------------------- /test/specs/validators/patternProperties.spec.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | var requireUncached = require('require-uncached') 3 | var patternPropertiesValidator = requireUncached('../../../src/validators/patternProperties') 4 | var propertyRules = require('../../../src/property-rules') 5 | var Ajv = require('ajv') 6 | 7 | var ajv = new Ajv() 8 | var expect = chai.expect 9 | 10 | var validator 11 | describe('patternPropertiesValidator', function() { 12 | describe('with additionalProperties', function() { 13 | var schema = { 14 | type: 'object', 15 | properties: { 16 | ab12: { 17 | type: 'string' 18 | } 19 | }, 20 | patternProperties: { 21 | '^abc.+$': { 22 | type: 'object', 23 | properties: { 24 | name: { 25 | type: 'string' 26 | } 27 | }, 28 | required: ['name'] 29 | }, 30 | '^.+123$': { 31 | type: 'number', 32 | minimum: 3 33 | } 34 | }, 35 | additionalProperties: { 36 | type: ['string', 'number'] 37 | } 38 | } 39 | beforeEach(function() { 40 | validator = patternPropertiesValidator(schema, schema.patternProperties, propertyRules.getPropertyValidationRules) 41 | }) 42 | 43 | it('is valid if present in properties', function() { 44 | assert(schema, { 45 | ab12: 'fdsf' 46 | }, true) 47 | }) 48 | 49 | it('is valid if no keys are valid and present additionalProperties', function() { 50 | assert(schema, { 51 | 'ab-12': 'fdsf' 52 | }, true) 53 | }) 54 | 55 | it('is invalid if matching first and not object', function() { 56 | assert(schema, { 57 | 'abcd': 1 58 | }, false) 59 | }) 60 | 61 | it('is invalid if matching second and not minimum 3', function() { 62 | assert(schema, { 63 | 'ab123': 1 64 | }, false) 65 | }) 66 | 67 | it('is valid if matching second and minimum 3', function() { 68 | assert(schema, { 69 | 'ab123': 3 70 | }, true) 71 | }) 72 | 73 | it('is invalid if matching first and object missing name', function() { 74 | assert(schema, { 75 | 'abc1': {} 76 | }, false) 77 | }) 78 | 79 | it('is valid if matching first and object is also valid', function() { 80 | assert(schema, { 81 | 'abc1': { 82 | name: 'john' 83 | } 84 | }, true) 85 | }) 86 | }) 87 | 88 | describe('with additionalProperties undefined', function() { 89 | var schema = { 90 | type: 'object', 91 | properties: { 92 | ab12: { 93 | type: 'string' 94 | } 95 | }, 96 | patternProperties: { 97 | '^abc.+$': { 98 | type: 'object', 99 | properties: { 100 | name: { 101 | type: 'string' 102 | } 103 | }, 104 | required: ['name'] 105 | }, 106 | '^.+123$': { 107 | type: 'number', 108 | minimum: 3 109 | } 110 | } 111 | } 112 | beforeEach(function() { 113 | validator = patternPropertiesValidator(schema, schema.patternProperties, propertyRules.getPropertyValidationRules) 114 | }) 115 | 116 | it('is valid if present in properties', function() { 117 | assert(schema, { 118 | ab12: 'fdsf' 119 | }, true) 120 | }) 121 | 122 | it('is valid if no keys are matching, since additionalProperties undefined (same as true) catches that', function() { 123 | assert(schema, { 124 | 'ab-12': 'fdsf' 125 | }, true) 126 | }) 127 | 128 | it('is invalid if matching first and not object', function() { 129 | assert(schema, { 130 | 'abcd': 1 131 | }, false) 132 | }) 133 | 134 | it('is invalid if matching second and not minimum 3', function() { 135 | assert(schema, { 136 | 'ab123': 1 137 | }, false) 138 | }) 139 | 140 | it('is valid if matching second and minimum 3', function() { 141 | assert(schema, { 142 | 'ab123': 3 143 | }, true) 144 | }) 145 | 146 | it('is invalid if matching first and object missing name', function() { 147 | assert(schema, { 148 | 'abc1': {} 149 | }, false) 150 | }) 151 | 152 | it('is valid if matching first and object is also valid', function() { 153 | assert(schema, { 154 | 'abc1': { 155 | name: 'john' 156 | } 157 | }, true) 158 | }) 159 | }) 160 | 161 | describe('with additionalProperties = true', function() { 162 | var schema = { 163 | type: 'object', 164 | properties: { 165 | ab12: { 166 | type: 'string' 167 | } 168 | }, 169 | patternProperties: { 170 | '^abc.+$': { 171 | type: 'object', 172 | properties: { 173 | name: { 174 | type: 'string' 175 | } 176 | }, 177 | required: ['name'] 178 | }, 179 | '^.+123$': { 180 | type: 'number', 181 | minimum: 3 182 | } 183 | }, 184 | additionalProperties: true 185 | } 186 | beforeEach(function() { 187 | validator = patternPropertiesValidator(schema, schema.patternProperties, propertyRules.getPropertyValidationRules) 188 | }) 189 | 190 | it('is valid if present in properties', function() { 191 | assert(schema, { 192 | ab12: 'fdsf' 193 | }, true) 194 | }) 195 | 196 | it('is valid if no keys are matching, since additionalProperties catches that', function() { 197 | assert(schema, { 198 | 'ab-12': 'fdsf' 199 | }, true) 200 | }) 201 | 202 | it('is invalid if matching first and not object', function() { 203 | assert(schema, { 204 | 'abcd': 1 205 | }, false) 206 | }) 207 | 208 | it('is invalid if matching second and not minimum 3', function() { 209 | assert(schema, { 210 | 'ab123': 1 211 | }, false) 212 | }) 213 | 214 | it('is valid if matching second and minimum 3', function() { 215 | assert(schema, { 216 | 'ab123': 3 217 | }, true) 218 | }) 219 | 220 | it('is invalid if matching first and object missing name', function() { 221 | assert(schema, { 222 | 'abc1': {} 223 | }, false) 224 | }) 225 | 226 | it('is valid if matching first and object is also valid', function() { 227 | assert(schema, { 228 | 'abc1': { 229 | name: 'john' 230 | } 231 | }, true) 232 | }) 233 | }) 234 | 235 | describe('with additionalProperties = false', function() { 236 | var schema = { 237 | type: 'object', 238 | properties: { 239 | ab12: { 240 | type: 'string' 241 | } 242 | }, 243 | patternProperties: { 244 | '^abc.+$': { 245 | type: 'object', 246 | properties: { 247 | name: { 248 | type: 'string' 249 | } 250 | }, 251 | required: ['name'] 252 | }, 253 | '^.+123$': { 254 | type: 'number', 255 | minimum: 3 256 | } 257 | }, 258 | additionalProperties: false 259 | } 260 | beforeEach(function() { 261 | validator = patternPropertiesValidator(schema, schema.patternProperties, propertyRules.getPropertyValidationRules) 262 | }) 263 | 264 | it('is valid if present in properties', function() { 265 | assert(schema, { 266 | ab12: 'fdsf' 267 | }, true) 268 | }) 269 | 270 | it('is invalid if no keys are matching since additionalProperties is false', function() { 271 | assert(schema, { 272 | 'ab-12': 'fdsf' 273 | }, false) 274 | }) 275 | 276 | it('is invalid if matching first and not object', function() { 277 | assert(schema, { 278 | 'abcd': 1 279 | }, false) 280 | }) 281 | 282 | it('is invalid if matching second and not minimum 3', function() { 283 | assert(schema, { 284 | 'ab123': 1 285 | }, false) 286 | }) 287 | 288 | it('is valid if matching second and minimum 3', function() { 289 | assert(schema, { 290 | 'ab123': 3 291 | }, true) 292 | }) 293 | 294 | it('is invalid if matching first and object missing name', function() { 295 | assert(schema, { 296 | 'abc1': {} 297 | }, false) 298 | }) 299 | 300 | it('is valid if matching first and object is also valid', function() { 301 | assert(schema, { 302 | 'abc1': { 303 | name: 'john' 304 | } 305 | }, true) 306 | }) 307 | }) 308 | }) 309 | 310 | function assert(schema, obj, expected) { 311 | if (ajv.validate(schema, obj) !== expected) { 312 | throw new Error('Test precondition failed, ajv and validator assertion are not equal.') 313 | } 314 | expect(validator(obj)).to.eql(expected) 315 | } 316 | --------------------------------------------------------------------------------