From d448bb96d7e6399cfc8335c30a538361716a7726 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Thu, 17 May 2018 19:03:12 +0200 Subject: [PATCH 01/13] Added validation contract validation Change-type: patch --- lib/schema/contract.json | 27 +++++++++ lib/validation.js | 63 ++++++++++++++++++++ package-lock.json | 57 ++++++++++-------- package.json | 1 + test/validation/validate-contracts.spec.js | 68 ++++++++++++++++++++++ 5 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 lib/schema/contract.json create mode 100644 lib/validation.js create mode 100644 test/validation/validate-contracts.spec.js diff --git a/lib/schema/contract.json b/lib/schema/contract.json new file mode 100644 index 0000000..3499e56 --- /dev/null +++ b/lib/schema/contract.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "contract.json", + "type": "object", + "properties": { + "slug": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "version": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "pattern": "^[a-z0-9-.]+$" + }, + "name": { + "type": "string", + "pattern": "^.*\\S.*$" + }, + "data": { + "type": "object" + } + }, + "required": [ "slug", "version", "type", "name" ] +} diff --git a/lib/validation.js b/lib/validation.js new file mode 100644 index 0000000..39a2abf --- /dev/null +++ b/lib/validation.js @@ -0,0 +1,63 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const _ = require('lodash') +const fs = require('fs') +const Ajv = require('ajv') +const ajv = new Ajv({ schemaId: '$id' }) +const baseContractSchema = require('./schema/contract.json') +ajv.addSchema(baseContractSchema) + +const mergeWithBase = (schema) => { + return { + '$schema': 'http://json-schema.org/draft-07/schema#', + '$id': schema.$id , + 'allOf': [ + { '$ref': 'contract.json' }, + schema + ] + } +} + +/** + * @module validation + */ + +/** + * @summary Checks if a contract is valid. + * @function + * @memberof module:validation + * @public + * + * } + */ +exports.checkValidContract = (contract, schema) => { + let success + if (schema) { + success = ajv.validate(mergeWithBase(schema), contract) + } else { + success = ajv.validate('contract.json', contract) + } + + if (success) { + return true + } else { + throw new Error(ajv.errorsText()) + return + } +} diff --git a/package-lock.json b/package-lock.json index ed0d902..ba76d2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "contrato", - "version": "0.0.4", + "version": "0.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -104,15 +104,21 @@ } }, "ajv": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", - "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", - "dev": true, + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", + "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", "json-schema-traverse": "0.3.1", - "json-stable-stringify": "1.0.1" + "uri-js": "4.2.1" + }, + "dependencies": { + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + } } }, "ajv-keywords": { @@ -1500,12 +1506,6 @@ } } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, "co-with-promise": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co-with-promise/-/co-with-promise-4.6.0.tgz", @@ -2035,7 +2035,6 @@ "integrity": "sha1-Ip7w41Tg5h2DfHqA/fuoJeGZgV4=", "dev": true, "requires": { - "ajv": "5.2.3", "babel-code-frame": "6.26.0", "chalk": "2.1.0", "concat-stream": "1.6.0", @@ -2417,18 +2416,17 @@ } } }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true - }, "fast-diff": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", "integrity": "sha1-S2LEK44D3j+EhGC2OQeZIGldAVQ=", "dev": true }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -4850,8 +4848,7 @@ "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, "json-stable-stringify": { "version": "1.0.1", @@ -7764,6 +7761,11 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -8596,7 +8598,6 @@ "integrity": "sha1-ozRHN1OR52atNNNIbm4q7chNLjY=", "dev": true, "requires": { - "ajv": "5.2.3", "ajv-keywords": "2.1.0", "chalk": "2.1.0", "lodash": "4.17.4", @@ -9052,6 +9053,14 @@ "xdg-basedir": "3.0.0" } }, + "uri-js": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.1.tgz", + "integrity": "sha512-jpKCA3HjsBfSDOEgxRDAxQCNyHfCPSbq57PqCkd3gAyBuPb3IWxw54EHncqESznIdqSetHfw3D7ylThu2Kcc9A==", + "requires": { + "punycode": "2.1.0" + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", diff --git a/package.json b/package.json index 27f1c77..c28e5fd 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "concurrency": 5 }, "dependencies": { + "ajv": "^6.5.0", "debug": "^3.0.1", "handlebars": "^4.0.10", "handlebars-helpers": "^0.10.0", diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js new file mode 100644 index 0000000..aa8d04e --- /dev/null +++ b/test/validation/validate-contracts.spec.js @@ -0,0 +1,68 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = { + 'slug': 'slug', + 'type': 'type', + 'version': 'version', + 'name': 'name', +} + +ava.test('should validate base contract', (test) => { + test.is(true, validation.checkValidContract(baseContract)) +}) + +ava.test('should reject invalid base contract', (test) => { + test.throws(() => { + validation.checkValidContract(_.omit(baseContract, 'slug')) + }, "data should have required property \'slug\'") +}) + +const extendedSchema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "extended.schema", + "type": 'object', + "properties": { + "test": { + "type": "string", + }, + "optional": { + "type": "string" + } + }, + "required": [ "test" ] +} + +const extendedContract = { + test: 'test' +} + +ava.test('should validate base contract', (test) => { + test.is(true, + validation.checkValidContract(_.merge(baseContract, extendedContract), extendedSchema)) +}) + +ava.test('Should reject invald extended contract', (test) => { + test.throws(() => { + validation.checkValidContract(extendedContract, extendedSchema) + }, 'data should have required property \'slug\'') +}) From 9573fea3fe7dfcd2146a4f74bbde6fc12869a1ff Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Thu, 17 May 2018 20:01:23 +0200 Subject: [PATCH 02/13] Added more test --- test/validation/validate-contracts.spec.js | 76 ++++++++++++++++++---- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index aa8d04e..0d8609b 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -38,27 +38,27 @@ ava.test('should reject invalid base contract', (test) => { }) const extendedSchema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "extended.schema", - "type": 'object', - "properties": { - "test": { - "type": "string", + '$schema': 'http://json-schema.org/draft-07/schema#', + '$id': 'extended.schema', + 'type': 'object', + 'properties': { + 'test': { + 'type': 'string', }, - "optional": { - "type": "string" + 'optional': { + 'type': 'string' } }, - "required": [ "test" ] + 'required': [ 'test' ] } const extendedContract = { test: 'test' } -ava.test('should validate base contract', (test) => { +ava.test('should validate extended contract', (test) => { test.is(true, - validation.checkValidContract(_.merge(baseContract, extendedContract), extendedSchema)) + validation.checkValidContract(_.merge({}, baseContract, extendedContract), extendedSchema)) }) ava.test('Should reject invald extended contract', (test) => { @@ -66,3 +66,57 @@ ava.test('Should reject invald extended contract', (test) => { validation.checkValidContract(extendedContract, extendedSchema) }, 'data should have required property \'slug\'') }) + +ava.test('Should reject invald extended contract', (test) => { + test.throws(() => { + validation.checkValidContract(baseContract, extendedSchema) + }, 'data should have required property \'test\'') +}) + +const nestedSchema = { + '$schema': 'http://json-schema.org/draft-07/schema#', + '$id': 'nested.schema', + 'type': 'object', + 'properties': { + 'data': { + 'type': 'object', + 'properties': { + 'test': { + 'type': 'string', + }, + 'optional': { + 'type': 'string' + } + }, + 'required': [ 'test' ] + } + }, + 'required': [ 'data' ] +} + +const nestedContract = { + data: { test: 'test' } +} + +ava.test('should validate nested contract', (test) => { + test.is(true, + validation.checkValidContract(_.merge({}, baseContract, nestedContract), nestedSchema)) +}) + +ava.test('Should reject invald nested contract', (test) => { + test.throws(() => { + validation.checkValidContract(nestedContract, nestedSchema) + }, 'data should have required property \'slug\'') +}) + +ava.test('Should reject invald nested contract', (test) => { + test.throws(() => { + validation.checkValidContract(baseContract, nestedSchema) + }, 'data should have required property \'data\'') +}) + +ava.test('Should reject invald nested contract', (test) => { + test.throws(() => { + validation.checkValidContract(_.merge({}, baseContract, { data: {} }), nestedSchema) + }, 'data.data should have required property \'test\'') +}) From dfec4416e1c50692f2303f32d08c55f1abd709b2 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Thu, 17 May 2018 20:15:21 +0200 Subject: [PATCH 03/13] Fixed some eslint errors --- lib/validation.js | 22 ++++---- test/validation/validate-contracts.spec.js | 64 ++++++++++++---------- 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/lib/validation.js b/lib/validation.js index 39a2abf..aec76ea 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -16,19 +16,21 @@ 'use strict' -const _ = require('lodash') -const fs = require('fs') const Ajv = require('ajv') -const ajv = new Ajv({ schemaId: '$id' }) +const ajv = new Ajv({ + schemaId: '$id' +}) const baseContractSchema = require('./schema/contract.json') ajv.addSchema(baseContractSchema) const mergeWithBase = (schema) => { return { - '$schema': 'http://json-schema.org/draft-07/schema#', - '$id': schema.$id , - 'allOf': [ - { '$ref': 'contract.json' }, + $schema: 'http://json-schema.org/draft-07/schema#', + $id: schema.$id, + allOf: [ + { + $ref: 'contract.json' + }, schema ] } @@ -47,7 +49,7 @@ const mergeWithBase = (schema) => { * } */ exports.checkValidContract = (contract, schema) => { - let success + let success = false if (schema) { success = ajv.validate(mergeWithBase(schema), contract) } else { @@ -56,8 +58,6 @@ exports.checkValidContract = (contract, schema) => { if (success) { return true - } else { - throw new Error(ajv.errorsText()) - return } + throw new Error(ajv.errorsText()) } diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index 0d8609b..8700f7a 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -21,10 +21,10 @@ const _ = require('lodash') const validation = require('../../lib/validation') const baseContract = { - 'slug': 'slug', - 'type': 'type', - 'version': 'version', - 'name': 'name', + slug: 'slug', + type: 'type', + version: 'version', + name: 'name' } ava.test('should validate base contract', (test) => { @@ -33,23 +33,23 @@ ava.test('should validate base contract', (test) => { ava.test('should reject invalid base contract', (test) => { test.throws(() => { - validation.checkValidContract(_.omit(baseContract, 'slug')) - }, "data should have required property \'slug\'") + validation.checkValidContract(_.omit(baseContract, 'slug')) + }, 'data should have required property \'slug\'') }) const extendedSchema = { - '$schema': 'http://json-schema.org/draft-07/schema#', - '$id': 'extended.schema', - 'type': 'object', - 'properties': { - 'test': { - 'type': 'string', + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'extended.schema', + type: 'object', + properties: { + test: { + type: 'string' }, - 'optional': { - 'type': 'string' + optional: { + type: 'string' } }, - 'required': [ 'test' ] + required: [ 'test' ] } const extendedContract = { @@ -74,28 +74,30 @@ ava.test('Should reject invald extended contract', (test) => { }) const nestedSchema = { - '$schema': 'http://json-schema.org/draft-07/schema#', - '$id': 'nested.schema', - 'type': 'object', - 'properties': { - 'data': { - 'type': 'object', - 'properties': { - 'test': { - 'type': 'string', + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'nested.schema', + type: 'object', + properties: { + data: { + type: 'object', + properties: { + test: { + type: 'string' }, - 'optional': { - 'type': 'string' + optional: { + type: 'string' } }, - 'required': [ 'test' ] + required: [ 'test' ] } }, - 'required': [ 'data' ] + required: [ 'data' ] } const nestedContract = { - data: { test: 'test' } + data: { + test: 'test' + } } ava.test('should validate nested contract', (test) => { @@ -117,6 +119,8 @@ ava.test('Should reject invald nested contract', (test) => { ava.test('Should reject invald nested contract', (test) => { test.throws(() => { - validation.checkValidContract(_.merge({}, baseContract, { data: {} }), nestedSchema) + validation.checkValidContract(_.merge({}, baseContract, { + data: {} + }), nestedSchema) }, 'data.data should have required property \'test\'') }) From 7a2742189b8314eddcf4f6d1b3934e5de509d353 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 01:20:24 +0200 Subject: [PATCH 04/13] Added more tests --- test/validation/validate-contracts.spec.js | 61 +++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index 8700f7a..e212c0a 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -73,9 +73,9 @@ ava.test('Should reject invald extended contract', (test) => { }, 'data should have required property \'test\'') }) -const nestedSchema = { +const overlappingSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'nested.schema', + $id: 'overlapping.schema', type: 'object', properties: { data: { @@ -94,33 +94,68 @@ const nestedSchema = { required: [ 'data' ] } -const nestedContract = { +const overlappingContract = { data: { test: 'test' } } -ava.test('should validate nested contract', (test) => { +ava.test('should validate overlapping contract', (test) => { test.is(true, - validation.checkValidContract(_.merge({}, baseContract, nestedContract), nestedSchema)) + validation.checkValidContract(_.merge({}, baseContract, overlappingContract), overlappingSchema)) }) -ava.test('Should reject invald nested contract', (test) => { +ava.test('Should reject invald overlapping contract', (test) => { test.throws(() => { - validation.checkValidContract(nestedContract, nestedSchema) + validation.checkValidContract(overlappingContract, overlappingSchema) }, 'data should have required property \'slug\'') }) -ava.test('Should reject invald nested contract', (test) => { +ava.test('Should reject invald overlapping contract', (test) => { test.throws(() => { - validation.checkValidContract(baseContract, nestedSchema) + validation.checkValidContract(baseContract, overlappingSchema) }, 'data should have required property \'data\'') }) -ava.test('Should reject invald nested contract', (test) => { +ava.test('Should reject invald overlapping contract', (test) => { test.throws(() => { - validation.checkValidContract(_.merge({}, baseContract, { - data: {} - }), nestedSchema) + validation.checkValidContract(_.merge({}, + baseContract, + _.omit(overlappingContract, 'data.test')), overlappingSchema) }, 'data.data should have required property \'test\'') }) + +const referencingSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'referencing.schema', + type: 'object', + properties: { + data: + { + $ref: 'contract.json' + } + }, + required: [ 'data' ] +} + +const referencingContract = { + data: { + slug: 'slug', + type: 'type', + version: 'version', + name: 'name' + } +} + +ava.test('should validate self referencing contract', (test) => { + test.is(true, + validation.checkValidContract(_.merge({}, baseContract, referencingContract), referencingSchema)) +}) + +ava.test('Should reject invald overlapping contract', (test) => { + test.throws(() => { + validation.checkValidContract(_.merge({}, + baseContract, + _.omit(referencingContract, 'data.slug')), referencingSchema) + }, 'data.data should have required property \'slug\'') +}) From 5f8017027ff46032c1eab24a04bcfb6ce43bb55d Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 02:22:20 +0200 Subject: [PATCH 05/13] Updated contrct.json schema --- lib/schema/contract.json | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/lib/schema/contract.json b/lib/schema/contract.json index 3499e56..fb5390a 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -2,6 +2,27 @@ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "contract.json", "type": "object", + + "definitions": { + "requireTemplate": { + "$id": "requireTemplate", + "type": "object", + "anyOf": [ + { "$ref": "contract.json#/properties/type" }, + { "$ref": "contract.json#/properties/slug" }, + { "$ref": "contract.json#/properties/version" } + ] + }, + "variantsTemplate": { + "$id": "variantsTemplate", + "type": "object", + "anyOf": [ + { "$ref": "contract.json#/properties/type" }, + { "$ref": "contract.json#/properties/version" } + ] + } + }, + "properties": { "slug": { "type": "string", @@ -21,6 +42,48 @@ }, "data": { "type": "object" + }, + "requires": { + "type": "array", + "items": { + "type": "object", + "anyOf": [ + { + "$ref": "#/definitions/requireTemplate" + }, + { + "properties": { + "or": { + "type": "array", + "items": { + "$ref": "#/definitions/requireTemplate" + } + } + } + } + ] + } + }, + "variants": { + "type": "array", + "items": { + "type": "object", + "anyOf": [ + { + "$ref": "#/definitions/variantsTemplate" + }, + { + "properties": { + "variants": { + "type": "array", + "items": { + "$ref": "#/definitions/variantsTemplate" + } + } + } + } + ] + } } }, "required": [ "slug", "version", "type", "name" ] From 9956ce0e06e4ff2e7a3d2709d99c4dc62617d144 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 11:22:33 +0200 Subject: [PATCH 06/13] Extended contract spec --- lib/schema/contract.json | 11 ++++++++++ test/validation/validate-contracts.spec.js | 24 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/lib/schema/contract.json b/lib/schema/contract.json index fb5390a..a810581 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -45,6 +45,7 @@ }, "requires": { "type": "array", + "additionalItems": false, "items": { "type": "object", "anyOf": [ @@ -66,6 +67,7 @@ }, "variants": { "type": "array", + "additionalItems": false, "items": { "type": "object", "anyOf": [ @@ -84,6 +86,15 @@ } ] } + }, + "tags": { + "type": "array", + "uniqueItems": true, + "additionalItems": false, + "items": { + "type": "string", + "pattern": "^[\\S]+(?: [\\S]+)*$" + } } }, "required": [ "slug", "version", "type", "name" ] diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index e212c0a..66ad2ee 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -159,3 +159,27 @@ ava.test('Should reject invald overlapping contract', (test) => { _.omit(referencingContract, 'data.slug')), referencingSchema) }, 'data.data should have required property \'slug\'') }) + +const taggedContract = { + tags: [ 'valid' ] +} + +ava.test('Should reject invald tagged contract', (test) => { + test.is(true, validation.checkValidContract(_.merge({}, baseContract, taggedContract))) +}) + +ava.test('Should reject invald tagged contract', (test) => { + taggedContract.tags.push('valid') + + test.throws(() => { + validation.checkValidContract(_.merge({}, baseContract, taggedContract)) + }, 'data.tags should NOT have duplicate items (items ## 1 and 0 are identical)') +}) + +ava.test('Should reject invald tagged contract', (test) => { + taggedContract.tags.push(' non valid ') + + test.throws(() => { + validation.checkValidContract(_.merge({}, baseContract, taggedContract)) + }, 'data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"') +}) From 43960b6832b468d70a1c4e87c4d5341e4a5c7ec5 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 12:29:43 +0200 Subject: [PATCH 07/13] Added assets to contrat.json --- lib/schema/contract.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/schema/contract.json b/lib/schema/contract.json index a810581..4289c04 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -95,7 +95,33 @@ "type": "string", "pattern": "^[\\S]+(?: [\\S]+)*$" } + }, + "assets": { + "type": "object", + "properties": { + "url": { + "type": "string", + "pattern": "^[a-z0-9-]+:\/\/(([a-z0-9-.])+\/)*[a-z0-9-.]+$" + }, + "name": { + "type": "string", + "pattern": "[a-z0-9-.]+$" + }, + "checksum": { + "type": "string", + "minLength": 1 + }, + "checksumType": { + "type": "string", + "enum": [ "sha256" ] + } + }, + "required": [ "url" ], + "dependencies": { + "checksum": [ "checksumType" ] + } } }, + "required": [ "slug", "version", "type", "name" ] } From 8ac4991b61cd77955a7b7264c1acfdfb7a79c179 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 18:12:50 +0200 Subject: [PATCH 08/13] Added all contract properties defined in the spec --- lib/schema/contract.json | 262 +++++++++++++-------- test/validation/validate-contracts.spec.js | 259 +++++++++++++++----- 2 files changed, 365 insertions(+), 156 deletions(-) diff --git a/lib/schema/contract.json b/lib/schema/contract.json index 4289c04..6d8a02b 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -1,127 +1,189 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "contract.json", - "type": "object", "definitions": { - "requireTemplate": { - "$id": "requireTemplate", + "baseContract": { + "$id": "baseContract", "type": "object", - "anyOf": [ - { "$ref": "contract.json#/properties/type" }, - { "$ref": "contract.json#/properties/slug" }, - { "$ref": "contract.json#/properties/version" } - ] - }, - "variantsTemplate": { - "$id": "variantsTemplate", - "type": "object", - "anyOf": [ - { "$ref": "contract.json#/properties/type" }, - { "$ref": "contract.json#/properties/version" } - ] - } - }, - - "properties": { - "slug": { - "type": "string", - "pattern": "^[a-z0-9-]+$" - }, - "version": { - "type": "string", - "minLength": 1 - }, - "type": { - "type": "string", - "pattern": "^[a-z0-9-.]+$" - }, - "name": { - "type": "string", - "pattern": "^.*\\S.*$" - }, - "data": { - "type": "object" - }, - "requires": { - "type": "array", - "additionalItems": false, - "items": { - "type": "object", - "anyOf": [ - { - "$ref": "#/definitions/requireTemplate" - }, - { + "additionalProperties": false, + "properties": { + "slug": { + "type": "string", + "pattern": "^[a-z0-9-]+$" + }, + "version": { + "type": "string", + "minLength": 1 + }, + "componentVersion": { + "type": "string", + "minLength": 1 + }, + "type": { + "type": "string", + "pattern": "^[a-z0-9-.]+$" + }, + "name": { + "type": "string", + "pattern": "^.*\\S.*$" + }, + "aliases": { + "type": "array", + "additionalItems": false, + "uniqueItems": true, + "items": { + "$ref": "#/properties/slug" + } + }, + "data": { + "type": "object" + }, + "extends": { + "allOf": [ + { + "$ref": "#" + }, + { + "required": [ "slug", "aliases" ] + } + ] + }, + "requires": { + "$ref": "contract.json#/definitions/recursiveOR" + }, + "tags": { + "type": "array", + "uniqueItems": true, + "additionalItems": false, + "items": { + "type": "string", + "pattern": "^[\\S]+(?: [\\S]+)*$" + } + }, + "capabilities": { + "type": "array", + "additionalItems": false, + "items": { + "type": "object", + "additionalProperties": false, "properties": { - "or": { - "type": "array", - "items": { - "$ref": "#/definitions/requireTemplate" + "slug": { + "$ref": "#/properties/slug" + }, + "componentVersion": { + "$ref": "#/properties/componentVersion" + } + }, + "required": [ "slug" ] + } + }, + "conflicts": { + "type": "array", + "additionalItems": false, + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "slug": { + "$ref": "#/properties/slug" + }, + "version": { + "$ref": "#/properties/version" + } + }, + "required": [ "slug" ] + } + }, + "assets": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.*$": { + "type": "object", + "properties": { + "url": { + "type": "string", + "pattern": "^[a-z0-9-]+:\/\/(([a-z0-9-.])+\/)*[a-z0-9-.]+$" + }, + "name": { + "type": "string", + "pattern": "[a-z0-9-.]+$" + }, + "checksum": { + "type": "string", + "minLength": 1 + }, + "checksumType": { + "type": "string", + "enum": [ "sha256" ] } + }, + "required": [ "url" ], + "dependencies": { + "checksum": [ "checksumType" ] } } } - ] + }, + "children": { + "type": "object" + } } }, - "variants": { + + "partialContract": { + "$id": "partialContract", + "type": "object", + "allOf": [ + { + "$ref": "contract.json#/definitions/baseContract" + }, + { + "dependencies": { + "version": [ "slug" ] + } + } + ] + }, + + "recursiveOR": { + "$id": "recursiveOR", "type": "array", "additionalItems": false, "items": { - "type": "object", "anyOf": [ { - "$ref": "#/definitions/variantsTemplate" - }, - { + "type": "object", + "additionalProperties": false, "properties": { - "variants": { - "type": "array", - "items": { - "$ref": "#/definitions/variantsTemplate" - } + "or": { + "$ref": "recursiveOR" } } + }, + { + "$ref": "contract.json#/definitions/partialContract" } ] } - }, - "tags": { - "type": "array", - "uniqueItems": true, - "additionalItems": false, - "items": { - "type": "string", - "pattern": "^[\\S]+(?: [\\S]+)*$" - } - }, - "assets": { - "type": "object", - "properties": { - "url": { - "type": "string", - "pattern": "^[a-z0-9-]+:\/\/(([a-z0-9-.])+\/)*[a-z0-9-.]+$" - }, - "name": { - "type": "string", - "pattern": "[a-z0-9-.]+$" - }, - "checksum": { - "type": "string", - "minLength": 1 - }, - "checksumType": { - "type": "string", - "enum": [ "sha256" ] - } - }, - "required": [ "url" ], - "dependencies": { - "checksum": [ "checksumType" ] - } } }, - - "required": [ "slug", "version", "type", "name" ] + + "allOf": [ + { + "$ref": "#/definitions/baseContract" + }, + { + "required": [ + "slug", + "version", + "type", + "componentVersion", + "aliases", + "tags", + "data" + ] + } + ] } diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index 66ad2ee..e6859e6 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -24,7 +24,10 @@ const baseContract = { slug: 'slug', type: 'type', version: 'version', - name: 'name' + componentVersion: 'componentVersion', + aliases: [], + data: {}, + tags: [] } ava.test('should validate base contract', (test) => { @@ -34,7 +37,7 @@ ava.test('should validate base contract', (test) => { ava.test('should reject invalid base contract', (test) => { test.throws(() => { validation.checkValidContract(_.omit(baseContract, 'slug')) - }, 'data should have required property \'slug\'') + }, 'data should have required property \'.slug\'') }) const extendedSchema = { @@ -42,35 +45,36 @@ const extendedSchema = { $id: 'extended.schema', type: 'object', properties: { - test: { - type: 'string' - }, - optional: { - type: 'string' + data: { + type: 'object', + properties: { + test: { + type: 'string' + }, + optional: { + type: 'string' + } + }, + required: [ 'test' ] } - }, - required: [ 'test' ] -} - -const extendedContract = { - test: 'test' + } } -ava.test('should validate extended contract', (test) => { - test.is(true, - validation.checkValidContract(_.merge({}, baseContract, extendedContract), extendedSchema)) +const extendedContract = _.merge({}, baseContract, { + data: + { + test: 'test' + } }) -ava.test('Should reject invald extended contract', (test) => { - test.throws(() => { - validation.checkValidContract(extendedContract, extendedSchema) - }, 'data should have required property \'slug\'') +ava.test('should validate extended contract', (test) => { + test.is(true, validation.checkValidContract(extendedContract, extendedSchema)) }) ava.test('Should reject invald extended contract', (test) => { test.throws(() => { validation.checkValidContract(baseContract, extendedSchema) - }, 'data should have required property \'test\'') + }, 'data.data should have required property \'test\'') }) const overlappingSchema = { @@ -94,34 +98,25 @@ const overlappingSchema = { required: [ 'data' ] } -const overlappingContract = { +const overlappingContract = _.merge({}, baseContract, { data: { test: 'test' } -} - -ava.test('should validate overlapping contract', (test) => { - test.is(true, - validation.checkValidContract(_.merge({}, baseContract, overlappingContract), overlappingSchema)) }) -ava.test('Should reject invald overlapping contract', (test) => { - test.throws(() => { - validation.checkValidContract(overlappingContract, overlappingSchema) - }, 'data should have required property \'slug\'') +ava.test('should validate overlapping contract', (test) => { + test.is(true, validation.checkValidContract(overlappingContract, overlappingSchema)) }) ava.test('Should reject invald overlapping contract', (test) => { test.throws(() => { validation.checkValidContract(baseContract, overlappingSchema) - }, 'data should have required property \'data\'') + }, 'data.data should have required property \'test\'') }) ava.test('Should reject invald overlapping contract', (test) => { test.throws(() => { - validation.checkValidContract(_.merge({}, - baseContract, - _.omit(overlappingContract, 'data.test')), overlappingSchema) + validation.checkValidContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) }, 'data.data should have required property \'test\'') }) @@ -138,41 +133,33 @@ const referencingSchema = { required: [ 'data' ] } -const referencingContract = { - data: { - slug: 'slug', - type: 'type', - version: 'version', - name: 'name' - } -} +const referencingContract = _.merge({}, baseContract, { + data: baseContract +}) ava.test('should validate self referencing contract', (test) => { - test.is(true, - validation.checkValidContract(_.merge({}, baseContract, referencingContract), referencingSchema)) + test.is(true, validation.checkValidContract(referencingContract, referencingSchema)) }) -ava.test('Should reject invald overlapping contract', (test) => { +ava.test('Should reject invald self referencing contract', (test) => { test.throws(() => { - validation.checkValidContract(_.merge({}, - baseContract, - _.omit(referencingContract, 'data.slug')), referencingSchema) - }, 'data.data should have required property \'slug\'') + validation.checkValidContract(_.omit(referencingContract, 'data.slug'), referencingSchema) + }, 'data.data should have required property \'.slug\'') }) -const taggedContract = { +const taggedContract = _.merge({}, baseContract, { tags: [ 'valid' ] -} +}) -ava.test('Should reject invald tagged contract', (test) => { - test.is(true, validation.checkValidContract(_.merge({}, baseContract, taggedContract))) +ava.test('Should validate tagged contract', (test) => { + test.is(true, validation.checkValidContract(taggedContract)) }) ava.test('Should reject invald tagged contract', (test) => { taggedContract.tags.push('valid') test.throws(() => { - validation.checkValidContract(_.merge({}, baseContract, taggedContract)) + validation.checkValidContract(taggedContract) }, 'data.tags should NOT have duplicate items (items ## 1 and 0 are identical)') }) @@ -180,6 +167,166 @@ ava.test('Should reject invald tagged contract', (test) => { taggedContract.tags.push(' non valid ') test.throws(() => { - validation.checkValidContract(_.merge({}, baseContract, taggedContract)) + validation.checkValidContract(taggedContract) }, 'data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"') }) + +const requireContract = _.merge({}, baseContract, { + requires: [ + { + or: [ + { type: 'type' }, + { + or: [ + { + slug: 'slug' + } + ] + } + ] + } + ] +}) + +const externalRequireContract = _.merge({}, baseContract, { + requires: [ + { data: + { + a: 'a' + } + } + ] +}) + +const badExternalRequireContract = _.merge({}, baseContract, { + requires: [ + { + or: [ + { type: 'type' }, + { + or: [ + { + a: 'a' + } + ] + } + ] + } + ] +}) + +const badExternalRequireContract2 = _.merge({}, baseContract, { + requires: [ + { + or: [ + { version: 'version' }, + ] + } + ] +}) + +ava.test('should validate require contract', (test) => { + test.is(true, validation.checkValidContract(requireContract)) +}) + +ava.test('should validate require contract with unknown fields', (test) => { + test.is(true, validation.checkValidContract(externalRequireContract)) +}) + +ava.test('should reject bad require contract with unknown fields', (test) => { + test.throws(() => { + validation.checkValidContract(badExternalRequireContract) + }) +}) + +ava.test('should reject bad require contract that only specifies version', (test) => { + test.throws(() => { + validation.checkValidContract(badExternalRequireContract2) + }) +}) + +const capabilitiesContract = _.merge({}, baseContract, { + capabilities: [ + { + 'slug': 'slug', + 'componentVersion': 'componentVersion' + }, + { + 'slug': 'slug' + } + ] +}) + +const badCapabilitiesContract = _.merge({}, baseContract, { + capabilities: [ + { + 'componentVersion': 'componentVersion' + } + ] +}) + +ava.test('should validate capabilities contract', (test) => { + test.is(true, validation.checkValidContract(capabilitiesContract)) +}) + +ava.test('should reject bad capabilities contract', (test) => { + test.throws(() => { + validation.checkValidContract(badCapabilitiesContract) + }, 'data.capabilities[0] should have required property \'slug\'') +}) + +const conflictsContract = _.merge({}, baseContract, { + conflicts: [ + { + 'slug': 'slug', + 'version': 'version' + }, + { + 'slug': 'slug' + } + ] +}) + +const badConflictsContract = _.merge({}, baseContract, { + conflicts: [ + { + 'a': 'a' + } + ] +}) + +ava.test('should validate conflicts contract', (test) => { + test.is(true, validation.checkValidContract(conflictsContract)) +}) + +ava.test('should reject bad conflicts contract', (test) => { + test.throws(() => { + validation.checkValidContract(badConflictsContract) + }, 'data.conflicts[0] should NOT have additional properties') +}) + +const assetsContract = _.merge({}, baseContract, { + assets: { + a: { + url: 'https://test.url', + checksum: 'checksum', + checksumType: 'sha256' + } + } +}) + +ava.test('should validate assets contract', (test) => { + test.is(true, validation.checkValidContract(assetsContract)) +}) + +ava.test('should reject bad assets contract', (test) => { + test.throws(() => { + validation.checkValidContract(_.omit(assetsContract, 'assets.a.url')) + }, 'data.assets[\'a\'] should have required property \'url\'') +}) + +ava.test('should reject bad assets contract', (test) => { + test.throws(() => { + validation.checkValidContract(_.omit(assetsContract, 'assets.a.checksumType')) + }, 'data.assets[\'a\'] should have property checksumType when property checksum is present') +}) From 229b094412899168df239348bfd6fe5d8ddbb03d Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Fri, 18 May 2018 18:29:46 +0200 Subject: [PATCH 09/13] Added children tests --- lib/schema/contract.json | 16 ++++++++- test/validation/validate-contracts.spec.js | 39 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/schema/contract.json b/lib/schema/contract.json index 6d8a02b..1e7049f 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -127,7 +127,21 @@ } }, "children": { - "type": "object" + "$id": "childNamespace", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.*$": { + "oneOf": [ + { + "$ref": "contract.json" + }, + { + "$ref": "childNamespace" + } + ] + } + } } } }, diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js index e6859e6..005510a 100644 --- a/test/validation/validate-contracts.spec.js +++ b/test/validation/validate-contracts.spec.js @@ -330,3 +330,42 @@ ava.test('should reject bad assets contract', (test) => { validation.checkValidContract(_.omit(assetsContract, 'assets.a.checksumType')) }, 'data.assets[\'a\'] should have property checksumType when property checksum is present') }) + +const fatherContract = _.merge({}, baseContract, { + children: { + base: baseContract + } +}) + +const grampaContract = _.merge({}, baseContract, { + children: { + father: { + child1: baseContract, + child2: baseContract + } + } +}) + +const badFamilyContract = _.merge({}, baseContract, { + children: { + a: { + b: { + c: true + } + } + } +}) + +ava.test('should validate father contract', (test) => { + test.is(true, validation.checkValidContract(fatherContract)) +}) + +ava.test('should validate grampa contract', (test) => { + test.is(true, validation.checkValidContract(grampaContract)) +}) + +ava.test('should reject bad family contract', (test) => { + test.throws(() => { + validation.checkValidContract(badFamilyContract) + }) +}) From 2f19cbfc13da7776b614724c183f6afe0a6b4c88 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Tue, 22 May 2018 14:58:18 +0200 Subject: [PATCH 10/13] Updated according to review --- lib/schema/contract.json | 19 +- lib/validation.js | 25 +- test/validation/common/baseContract.json | 9 + test/validation/validate-assets.spec.js | 52 +++ .../validate-base-contracts.spec.js | 36 ++ test/validation/validate-capabilities.spec.js | 56 +++ test/validation/validate-children.spec.js | 67 ++++ test/validation/validate-conflicts.spec.js | 55 +++ test/validation/validate-contracts.spec.js | 371 ------------------ .../validate-extended-schema.spec.js | 63 +++ .../validate-overlapping-schema.spec.js | 69 ++++ test/validation/validate-requires.spec.js | 101 +++++ ...validate-self-referencing-contract.spec.js | 53 +++ test/validation/validate-tags.spec.js | 50 +++ 14 files changed, 636 insertions(+), 390 deletions(-) create mode 100644 test/validation/common/baseContract.json create mode 100644 test/validation/validate-assets.spec.js create mode 100644 test/validation/validate-base-contracts.spec.js create mode 100644 test/validation/validate-capabilities.spec.js create mode 100644 test/validation/validate-children.spec.js create mode 100644 test/validation/validate-conflicts.spec.js delete mode 100644 test/validation/validate-contracts.spec.js create mode 100644 test/validation/validate-extended-schema.spec.js create mode 100644 test/validation/validate-overlapping-schema.spec.js create mode 100644 test/validation/validate-requires.spec.js create mode 100644 test/validation/validate-self-referencing-contract.spec.js create mode 100644 test/validation/validate-tags.spec.js diff --git a/lib/schema/contract.json b/lib/schema/contract.json index 1e7049f..3eaecec 100644 --- a/lib/schema/contract.json +++ b/lib/schema/contract.json @@ -14,7 +14,8 @@ }, "version": { "type": "string", - "minLength": 1 + "minLength": 1, + "pattern": "^[~\\^]*\\d+(\\.[~\\^]*\\d+(\\.[~\\^]*\\d+)*)*$" }, "componentVersion": { "type": "string", @@ -79,21 +80,7 @@ } }, "conflicts": { - "type": "array", - "additionalItems": false, - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "slug": { - "$ref": "#/properties/slug" - }, - "version": { - "$ref": "#/properties/version" - } - }, - "required": [ "slug" ] - } + "$ref": "contract.json#/definitions/recursiveOR" }, "assets": { "type": "object", diff --git a/lib/validation.js b/lib/validation.js index aec76ea..4dc86ad 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -46,9 +46,28 @@ const mergeWithBase = (schema) => { * @memberof module:validation * @public * + * @param {Object} contract - the contract to validate + * @param {Object} schema - optional schema + * @returns {Object} result + * + * @description + * This function will validate that the supplied contract is valid according to + * the json schema specification. Accepts an optional schema that can reference and extend + * the base schema. If a custom schema is passed, both the generic and custom validation + * rules will apply + * + * @example + * validation.checkContract({ + * slug: 'slug', + * type: 'type', + * version: '1', + * componentVersion: 'componentVersion', + * aliases: [], + * data: {}, + * tags: [] * } */ -exports.checkValidContract = (contract, schema) => { +exports.checkContract = (contract, schema) => { let success = false if (schema) { success = ajv.validate(mergeWithBase(schema), contract) @@ -57,7 +76,7 @@ exports.checkValidContract = (contract, schema) => { } if (success) { - return true + return { success: true, errors: [] } } - throw new Error(ajv.errorsText()) + return { success: false, errors: [ajv.errorsText()] } } diff --git a/test/validation/common/baseContract.json b/test/validation/common/baseContract.json new file mode 100644 index 0000000..c458c1f --- /dev/null +++ b/test/validation/common/baseContract.json @@ -0,0 +1,9 @@ +{ + "slug": "slug", + "type": "type", + "version": "1", + "componentVersion": "componentVersion", + "aliases": [], + "data": {}, + "tags": [] +} diff --git a/test/validation/validate-assets.spec.js b/test/validation/validate-assets.spec.js new file mode 100644 index 0000000..1451d39 --- /dev/null +++ b/test/validation/validate-assets.spec.js @@ -0,0 +1,52 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const assetsContract = _.merge({}, baseContract, { + assets: { + a: { + url: 'https://test.url', + checksum: 'checksum', + checksumType: 'sha256' + } + } +}) + +ava.test('should validate assets contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(assetsContract) + ) +}) + +ava.test('should reject bad assets contract', (test) => { + let { success, errors } = validation.checkContract(_.omit(assetsContract, 'assets.a.url')) + test.is(false, success) + test.is('data.assets[\'a\'] should have required property \'url\'', errors[0]) +}) + +ava.test('should reject bad assets contract', (test) => { + let { success, errors } = validation.checkContract(_.omit(assetsContract, 'assets.a.checksumType')) + test.is(false, success) + test.is('data.assets[\'a\'] should have property checksumType when property checksum is present', errors[0]) +}) diff --git a/test/validation/validate-base-contracts.spec.js b/test/validation/validate-base-contracts.spec.js new file mode 100644 index 0000000..3b28fb9 --- /dev/null +++ b/test/validation/validate-base-contracts.spec.js @@ -0,0 +1,36 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +ava.test('should validate base contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(baseContract) + ) +}) + +ava.test('should reject invalid base contract', (test) => { + let { success, errors } = validation.checkContract(_.omit(baseContract, 'slug')) + test.is(false, success) + test.is('data should have required property \'.slug\'', errors[0]) +}) diff --git a/test/validation/validate-capabilities.spec.js b/test/validation/validate-capabilities.spec.js new file mode 100644 index 0000000..1d1a708 --- /dev/null +++ b/test/validation/validate-capabilities.spec.js @@ -0,0 +1,56 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const capabilitiesContract = _.merge({}, baseContract, { + capabilities: [ + { + 'slug': 'slug', + 'componentVersion': 'componentVersion' + }, + { + 'slug': 'slug' + } + ] +}) + +const badCapabilitiesContract = _.merge({}, baseContract, { + capabilities: [ + { + 'componentVersion': 'componentVersion' + } + ] +}) + +ava.test('should validate capabilities contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(capabilitiesContract) + ) +}) + +ava.test('should reject bad capabilities contract', (test) => { + let { success, errors } = validation.checkContract(badCapabilitiesContract) + test.is(false, success) + test.is('data.capabilities[0] should have required property \'slug\'', errors[0]) +}) diff --git a/test/validation/validate-children.spec.js b/test/validation/validate-children.spec.js new file mode 100644 index 0000000..7d85be4 --- /dev/null +++ b/test/validation/validate-children.spec.js @@ -0,0 +1,67 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const fatherContract = _.merge({}, baseContract, { + children: { + base: baseContract + } +}) + +const grampaContract = _.merge({}, baseContract, { + children: { + father: { + child1: baseContract, + child2: baseContract + } + } +}) + +const badFamilyContract = _.merge({}, baseContract, { + children: { + a: { + b: { + c: true + } + } + } +}) + +ava.test('should validate father contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(fatherContract) + ) +}) + +ava.test('should validate grampa contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(grampaContract) + ) +}) + +ava.test('should reject bad family contract', (test) => { + let { success, errors } = validation.checkContract(badFamilyContract) + test.is(false, success) +}) diff --git a/test/validation/validate-conflicts.spec.js b/test/validation/validate-conflicts.spec.js new file mode 100644 index 0000000..89f2041 --- /dev/null +++ b/test/validation/validate-conflicts.spec.js @@ -0,0 +1,55 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const conflictsContract = _.merge({}, baseContract, { + conflicts: [ + { + slug: 'slug', + version: '1' + }, + { + 'slug': 'slug' + } + ] +}) + +const badConflictsContract = _.merge({}, baseContract, { + conflicts: [ + { + 'a': 'a' + } + ] +}) + +ava.test('should validate conflicts contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(conflictsContract) + ) +}) + +ava.test('should reject bad conflicts contract', (test) => { + let { success, errors } = validation.checkContract(badConflictsContract) + test.is(false, success) +}) diff --git a/test/validation/validate-contracts.spec.js b/test/validation/validate-contracts.spec.js deleted file mode 100644 index 005510a..0000000 --- a/test/validation/validate-contracts.spec.js +++ /dev/null @@ -1,371 +0,0 @@ -/* - * Copyright 2017 resin.io - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict' - -const ava = require('ava') -const _ = require('lodash') -const validation = require('../../lib/validation') - -const baseContract = { - slug: 'slug', - type: 'type', - version: 'version', - componentVersion: 'componentVersion', - aliases: [], - data: {}, - tags: [] -} - -ava.test('should validate base contract', (test) => { - test.is(true, validation.checkValidContract(baseContract)) -}) - -ava.test('should reject invalid base contract', (test) => { - test.throws(() => { - validation.checkValidContract(_.omit(baseContract, 'slug')) - }, 'data should have required property \'.slug\'') -}) - -const extendedSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'extended.schema', - type: 'object', - properties: { - data: { - type: 'object', - properties: { - test: { - type: 'string' - }, - optional: { - type: 'string' - } - }, - required: [ 'test' ] - } - } -} - -const extendedContract = _.merge({}, baseContract, { - data: - { - test: 'test' - } -}) - -ava.test('should validate extended contract', (test) => { - test.is(true, validation.checkValidContract(extendedContract, extendedSchema)) -}) - -ava.test('Should reject invald extended contract', (test) => { - test.throws(() => { - validation.checkValidContract(baseContract, extendedSchema) - }, 'data.data should have required property \'test\'') -}) - -const overlappingSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'overlapping.schema', - type: 'object', - properties: { - data: { - type: 'object', - properties: { - test: { - type: 'string' - }, - optional: { - type: 'string' - } - }, - required: [ 'test' ] - } - }, - required: [ 'data' ] -} - -const overlappingContract = _.merge({}, baseContract, { - data: { - test: 'test' - } -}) - -ava.test('should validate overlapping contract', (test) => { - test.is(true, validation.checkValidContract(overlappingContract, overlappingSchema)) -}) - -ava.test('Should reject invald overlapping contract', (test) => { - test.throws(() => { - validation.checkValidContract(baseContract, overlappingSchema) - }, 'data.data should have required property \'test\'') -}) - -ava.test('Should reject invald overlapping contract', (test) => { - test.throws(() => { - validation.checkValidContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) - }, 'data.data should have required property \'test\'') -}) - -const referencingSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'referencing.schema', - type: 'object', - properties: { - data: - { - $ref: 'contract.json' - } - }, - required: [ 'data' ] -} - -const referencingContract = _.merge({}, baseContract, { - data: baseContract -}) - -ava.test('should validate self referencing contract', (test) => { - test.is(true, validation.checkValidContract(referencingContract, referencingSchema)) -}) - -ava.test('Should reject invald self referencing contract', (test) => { - test.throws(() => { - validation.checkValidContract(_.omit(referencingContract, 'data.slug'), referencingSchema) - }, 'data.data should have required property \'.slug\'') -}) - -const taggedContract = _.merge({}, baseContract, { - tags: [ 'valid' ] -}) - -ava.test('Should validate tagged contract', (test) => { - test.is(true, validation.checkValidContract(taggedContract)) -}) - -ava.test('Should reject invald tagged contract', (test) => { - taggedContract.tags.push('valid') - - test.throws(() => { - validation.checkValidContract(taggedContract) - }, 'data.tags should NOT have duplicate items (items ## 1 and 0 are identical)') -}) - -ava.test('Should reject invald tagged contract', (test) => { - taggedContract.tags.push(' non valid ') - - test.throws(() => { - validation.checkValidContract(taggedContract) - }, 'data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"') -}) - -const requireContract = _.merge({}, baseContract, { - requires: [ - { - or: [ - { type: 'type' }, - { - or: [ - { - slug: 'slug' - } - ] - } - ] - } - ] -}) - -const externalRequireContract = _.merge({}, baseContract, { - requires: [ - { data: - { - a: 'a' - } - } - ] -}) - -const badExternalRequireContract = _.merge({}, baseContract, { - requires: [ - { - or: [ - { type: 'type' }, - { - or: [ - { - a: 'a' - } - ] - } - ] - } - ] -}) - -const badExternalRequireContract2 = _.merge({}, baseContract, { - requires: [ - { - or: [ - { version: 'version' }, - ] - } - ] -}) - -ava.test('should validate require contract', (test) => { - test.is(true, validation.checkValidContract(requireContract)) -}) - -ava.test('should validate require contract with unknown fields', (test) => { - test.is(true, validation.checkValidContract(externalRequireContract)) -}) - -ava.test('should reject bad require contract with unknown fields', (test) => { - test.throws(() => { - validation.checkValidContract(badExternalRequireContract) - }) -}) - -ava.test('should reject bad require contract that only specifies version', (test) => { - test.throws(() => { - validation.checkValidContract(badExternalRequireContract2) - }) -}) - -const capabilitiesContract = _.merge({}, baseContract, { - capabilities: [ - { - 'slug': 'slug', - 'componentVersion': 'componentVersion' - }, - { - 'slug': 'slug' - } - ] -}) - -const badCapabilitiesContract = _.merge({}, baseContract, { - capabilities: [ - { - 'componentVersion': 'componentVersion' - } - ] -}) - -ava.test('should validate capabilities contract', (test) => { - test.is(true, validation.checkValidContract(capabilitiesContract)) -}) - -ava.test('should reject bad capabilities contract', (test) => { - test.throws(() => { - validation.checkValidContract(badCapabilitiesContract) - }, 'data.capabilities[0] should have required property \'slug\'') -}) - -const conflictsContract = _.merge({}, baseContract, { - conflicts: [ - { - 'slug': 'slug', - 'version': 'version' - }, - { - 'slug': 'slug' - } - ] -}) - -const badConflictsContract = _.merge({}, baseContract, { - conflicts: [ - { - 'a': 'a' - } - ] -}) - -ava.test('should validate conflicts contract', (test) => { - test.is(true, validation.checkValidContract(conflictsContract)) -}) - -ava.test('should reject bad conflicts contract', (test) => { - test.throws(() => { - validation.checkValidContract(badConflictsContract) - }, 'data.conflicts[0] should NOT have additional properties') -}) - -const assetsContract = _.merge({}, baseContract, { - assets: { - a: { - url: 'https://test.url', - checksum: 'checksum', - checksumType: 'sha256' - } - } -}) - -ava.test('should validate assets contract', (test) => { - test.is(true, validation.checkValidContract(assetsContract)) -}) - -ava.test('should reject bad assets contract', (test) => { - test.throws(() => { - validation.checkValidContract(_.omit(assetsContract, 'assets.a.url')) - }, 'data.assets[\'a\'] should have required property \'url\'') -}) - -ava.test('should reject bad assets contract', (test) => { - test.throws(() => { - validation.checkValidContract(_.omit(assetsContract, 'assets.a.checksumType')) - }, 'data.assets[\'a\'] should have property checksumType when property checksum is present') -}) - -const fatherContract = _.merge({}, baseContract, { - children: { - base: baseContract - } -}) - -const grampaContract = _.merge({}, baseContract, { - children: { - father: { - child1: baseContract, - child2: baseContract - } - } -}) - -const badFamilyContract = _.merge({}, baseContract, { - children: { - a: { - b: { - c: true - } - } - } -}) - -ava.test('should validate father contract', (test) => { - test.is(true, validation.checkValidContract(fatherContract)) -}) - -ava.test('should validate grampa contract', (test) => { - test.is(true, validation.checkValidContract(grampaContract)) -}) - -ava.test('should reject bad family contract', (test) => { - test.throws(() => { - validation.checkValidContract(badFamilyContract) - }) -}) diff --git a/test/validation/validate-extended-schema.spec.js b/test/validation/validate-extended-schema.spec.js new file mode 100644 index 0000000..a578446 --- /dev/null +++ b/test/validation/validate-extended-schema.spec.js @@ -0,0 +1,63 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const extendedSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'extended.schema', + type: 'object', + properties: { + data: { + type: 'object', + properties: { + test: { + type: 'string' + }, + optional: { + type: 'string' + } + }, + required: [ 'test' ] + } + } +} + +const extendedContract = _.merge({}, baseContract, { + data: + { + test: 'test' + } +}) + +ava.test('should validate extended contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(extendedContract, extendedSchema) + ) +}) + +ava.test('Should reject invald extended contract', (test) => { + let { success, errors } = validation.checkContract(baseContract, extendedSchema) + test.is(false, success) + test.is('data.data should have required property \'test\'', errors[0]) +}) diff --git a/test/validation/validate-overlapping-schema.spec.js b/test/validation/validate-overlapping-schema.spec.js new file mode 100644 index 0000000..f8f6bfc --- /dev/null +++ b/test/validation/validate-overlapping-schema.spec.js @@ -0,0 +1,69 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const overlappingSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'overlapping.schema', + type: 'object', + properties: { + data: { + type: 'object', + properties: { + test: { + type: 'string' + }, + optional: { + type: 'string' + } + }, + required: [ 'test' ] + } + }, + required: [ 'data' ] +} + +const overlappingContract = _.merge({}, baseContract, { + data: { + test: 'test' + } +}) + +ava.test('should validate overlapping contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(overlappingContract, overlappingSchema) + ) +}) + +ava.test('Should reject invald overlapping contract', (test) => { + let { success, errors } = validation.checkContract(baseContract, overlappingSchema) + test.is(false, success) + test.is('data.data should have required property \'test\'', errors[0]) +}) + +ava.test('Should reject invald overlapping contract', (test) => { + let { success, errors } = validation.checkContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) + test.is(false, success) + test.is('data.data should have required property \'test\'', errors[0]) +}) diff --git a/test/validation/validate-requires.spec.js b/test/validation/validate-requires.spec.js new file mode 100644 index 0000000..0fb8af1 --- /dev/null +++ b/test/validation/validate-requires.spec.js @@ -0,0 +1,101 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const requireContract = _.merge({}, baseContract, { + requires: [ + { + or: [ + { type: 'type' }, + { + or: [ + { + slug: 'slug' + } + ] + } + ] + } + ] +}) + +const externalRequireContract = _.merge({}, baseContract, { + requires: [ + { data: + { + a: 'a' + } + } + ] +}) + +const badExternalRequireContract = _.merge({}, baseContract, { + requires: [ + { + or: [ + { type: 'type' }, + { + or: [ + { + a: 'a' + } + ] + } + ] + } + ] +}) + +const badExternalRequireContract2 = _.merge({}, baseContract, { + requires: [ + { + or: [ + { version: '1' }, + ] + } + ] +}) + +ava.test('should validate require contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(requireContract) + ) +}) + +ava.test('should validate require contract with unknown fields', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(externalRequireContract) + ) +}) + +ava.test('should reject bad require contract with unknown fields', (test) => { + let { success, errors } = validation.checkContract(badExternalRequireContract) + test.is(false, success) +}) + +ava.test('should reject bad require contract that only specifies version', (test) => { + let { success, errors } = validation.checkContract(badExternalRequireContract2) + test.is(false, success) +}) diff --git a/test/validation/validate-self-referencing-contract.spec.js b/test/validation/validate-self-referencing-contract.spec.js new file mode 100644 index 0000000..36e6191 --- /dev/null +++ b/test/validation/validate-self-referencing-contract.spec.js @@ -0,0 +1,53 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const referencingSchema = { + $schema: 'http://json-schema.org/draft-07/schema#', + $id: 'referencing.schema', + type: 'object', + properties: { + data: + { + $ref: 'contract.json' + } + }, + required: [ 'data' ] +} + +const referencingContract = _.merge({}, baseContract, { + data: baseContract +}) + +ava.test('should validate self referencing contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(referencingContract, referencingSchema) + ) +}) + +ava.test('Should reject invald self referencing contract', (test) => { + let { success, errors } = validation.checkContract(_.omit(referencingContract, 'data.slug'), referencingSchema) + test.is(false, success) + test.is('data.data should have required property \'.slug\'', errors[0]) +}) diff --git a/test/validation/validate-tags.spec.js b/test/validation/validate-tags.spec.js new file mode 100644 index 0000000..2ca34a0 --- /dev/null +++ b/test/validation/validate-tags.spec.js @@ -0,0 +1,50 @@ +/* + * Copyright 2017 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict' + +const ava = require('ava') +const _ = require('lodash') +const validation = require('../../lib/validation') + +const baseContract = require('./common/baseContract') + +const taggedContract = _.merge({}, baseContract, { + tags: [ 'valid' ] +}) + +ava.test('Should validate tagged contract', (test) => { + test.deepEqual( + { success: true, errors: [] }, + validation.checkContract(taggedContract) + ) +}) + +ava.test('Should reject invald tagged contract', (test) => { + taggedContract.tags.push('valid') + + let { success, errors } = validation.checkContract(taggedContract) + test.is(false, success) + test.is('data.tags should NOT have duplicate items (items ## 1 and 0 are identical)', errors[0]) +}) + +ava.test('Should reject invald tagged contract', (test) => { + taggedContract.tags.push(' non valid ') + + let { success, errors } = validation.checkContract(taggedContract) + test.is(false, success) + test.is('data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"', errors[0]) +}) From dfb0c612586f8c39876a28807497f55cda73bc78 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Wed, 30 May 2018 13:59:08 +0200 Subject: [PATCH 11/13] Export validation from main module --- lib/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/index.js b/lib/index.js index 0677b63..26b29d8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,6 +24,7 @@ exports.Contract = require('./contract') exports.Blueprint = require('./blueprint') exports.buildTemplate = require('./partials').buildTemplate +exports.validation = require('./validation') exports.query = (universe, layout, skeleton) => { const blueprint = new exports.Blueprint(layout, skeleton) From de6aacf9dcfb8b1e33f3f2d4f2332ce28882cbf4 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Wed, 30 May 2018 14:21:29 +0200 Subject: [PATCH 12/13] Fix eslint errors --- lib/validation.js | 18 ++++++++-- test/validation/validate-assets.spec.js | 18 +++++----- .../validate-base-contracts.spec.js | 10 +++--- test/validation/validate-capabilities.spec.js | 18 +++++----- test/validation/validate-children.spec.js | 18 ++++++---- test/validation/validate-conflicts.spec.js | 12 ++++--- .../validate-extended-schema.spec.js | 10 +++--- .../validate-overlapping-schema.spec.js | 16 +++++---- test/validation/validate-requires.spec.js | 36 ++++++++++++------- ...validate-self-referencing-contract.spec.js | 10 +++--- test/validation/validate-tags.spec.js | 16 +++++---- 11 files changed, 113 insertions(+), 69 deletions(-) diff --git a/lib/validation.js b/lib/validation.js index 4dc86ad..aa5ed68 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -23,6 +23,16 @@ const ajv = new Ajv({ const baseContractSchema = require('./schema/contract.json') ajv.addSchema(baseContractSchema) +// eslint-disable-next-line jsdoc/require-example +/** + * @summary Merge supplied schema with base contract schema + * @function + * @private + * + * @param {Object} schema - JSON schema + * @returns {Object} - Merged schema + * + */ const mergeWithBase = (schema) => { return { $schema: 'http://json-schema.org/draft-07/schema#', @@ -76,7 +86,11 @@ exports.checkContract = (contract, schema) => { } if (success) { - return { success: true, errors: [] } + return { + success: true, errors: [] + } + } + return { + success: false, errors: [ ajv.errorsText() ] } - return { success: false, errors: [ajv.errorsText()] } } diff --git a/test/validation/validate-assets.spec.js b/test/validation/validate-assets.spec.js index 1451d39..3f75bbc 100644 --- a/test/validation/validate-assets.spec.js +++ b/test/validation/validate-assets.spec.js @@ -24,7 +24,7 @@ const baseContract = require('./common/baseContract') const assetsContract = _.merge({}, baseContract, { assets: { - a: { + asset: { url: 'https://test.url', checksum: 'checksum', checksumType: 'sha256' @@ -34,19 +34,21 @@ const assetsContract = _.merge({}, baseContract, { ava.test('should validate assets contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(assetsContract) ) }) ava.test('should reject bad assets contract', (test) => { - let { success, errors } = validation.checkContract(_.omit(assetsContract, 'assets.a.url')) - test.is(false, success) - test.is('data.assets[\'a\'] should have required property \'url\'', errors[0]) + const result = validation.checkContract(_.omit(assetsContract, 'assets.asset.url')) + test.is(false, result.success) + test.is('data.assets[\'asset\'] should have required property \'url\'', result.errors[0]) }) ava.test('should reject bad assets contract', (test) => { - let { success, errors } = validation.checkContract(_.omit(assetsContract, 'assets.a.checksumType')) - test.is(false, success) - test.is('data.assets[\'a\'] should have property checksumType when property checksum is present', errors[0]) + const result = validation.checkContract(_.omit(assetsContract, 'assets.asset.checksumType')) + test.is(false, result.success) + test.is('data.assets[\'asset\'] should have property checksumType when property checksum is present', result.errors[0]) }) diff --git a/test/validation/validate-base-contracts.spec.js b/test/validation/validate-base-contracts.spec.js index 3b28fb9..146c1b8 100644 --- a/test/validation/validate-base-contracts.spec.js +++ b/test/validation/validate-base-contracts.spec.js @@ -24,13 +24,15 @@ const baseContract = require('./common/baseContract') ava.test('should validate base contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(baseContract) ) }) ava.test('should reject invalid base contract', (test) => { - let { success, errors } = validation.checkContract(_.omit(baseContract, 'slug')) - test.is(false, success) - test.is('data should have required property \'.slug\'', errors[0]) + const result = validation.checkContract(_.omit(baseContract, 'slug')) + test.is(false, result.success) + test.is('data should have required property \'.slug\'', result.errors[0]) }) diff --git a/test/validation/validate-capabilities.spec.js b/test/validation/validate-capabilities.spec.js index 1d1a708..def6e3e 100644 --- a/test/validation/validate-capabilities.spec.js +++ b/test/validation/validate-capabilities.spec.js @@ -25,11 +25,11 @@ const baseContract = require('./common/baseContract') const capabilitiesContract = _.merge({}, baseContract, { capabilities: [ { - 'slug': 'slug', - 'componentVersion': 'componentVersion' + slug: 'slug', + componentVersion: 'componentVersion' }, { - 'slug': 'slug' + slug: 'slug' } ] }) @@ -37,20 +37,22 @@ const capabilitiesContract = _.merge({}, baseContract, { const badCapabilitiesContract = _.merge({}, baseContract, { capabilities: [ { - 'componentVersion': 'componentVersion' + componentVersion: 'componentVersion' } ] }) ava.test('should validate capabilities contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(capabilitiesContract) ) }) ava.test('should reject bad capabilities contract', (test) => { - let { success, errors } = validation.checkContract(badCapabilitiesContract) - test.is(false, success) - test.is('data.capabilities[0] should have required property \'slug\'', errors[0]) + const result = validation.checkContract(badCapabilitiesContract) + test.is(false, result.success) + test.is('data.capabilities[0] should have required property \'slug\'', result.errors[0]) }) diff --git a/test/validation/validate-children.spec.js b/test/validation/validate-children.spec.js index 7d85be4..6348cf8 100644 --- a/test/validation/validate-children.spec.js +++ b/test/validation/validate-children.spec.js @@ -39,9 +39,9 @@ const grampaContract = _.merge({}, baseContract, { const badFamilyContract = _.merge({}, baseContract, { children: { - a: { - b: { - c: true + father: { + child: { + prop: true } } } @@ -49,19 +49,23 @@ const badFamilyContract = _.merge({}, baseContract, { ava.test('should validate father contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(fatherContract) ) }) ava.test('should validate grampa contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(grampaContract) ) }) ava.test('should reject bad family contract', (test) => { - let { success, errors } = validation.checkContract(badFamilyContract) - test.is(false, success) + const result = validation.checkContract(badFamilyContract) + test.is(false, result.success) }) diff --git a/test/validation/validate-conflicts.spec.js b/test/validation/validate-conflicts.spec.js index 89f2041..fa691d5 100644 --- a/test/validation/validate-conflicts.spec.js +++ b/test/validation/validate-conflicts.spec.js @@ -29,7 +29,7 @@ const conflictsContract = _.merge({}, baseContract, { version: '1' }, { - 'slug': 'slug' + slug: 'slug' } ] }) @@ -37,19 +37,21 @@ const conflictsContract = _.merge({}, baseContract, { const badConflictsContract = _.merge({}, baseContract, { conflicts: [ { - 'a': 'a' + prop: 'prop' } ] }) ava.test('should validate conflicts contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(conflictsContract) ) }) ava.test('should reject bad conflicts contract', (test) => { - let { success, errors } = validation.checkContract(badConflictsContract) - test.is(false, success) + const result = validation.checkContract(badConflictsContract) + test.is(false, result.success) }) diff --git a/test/validation/validate-extended-schema.spec.js b/test/validation/validate-extended-schema.spec.js index a578446..a8f304b 100644 --- a/test/validation/validate-extended-schema.spec.js +++ b/test/validation/validate-extended-schema.spec.js @@ -51,13 +51,15 @@ const extendedContract = _.merge({}, baseContract, { ava.test('should validate extended contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(extendedContract, extendedSchema) ) }) ava.test('Should reject invald extended contract', (test) => { - let { success, errors } = validation.checkContract(baseContract, extendedSchema) - test.is(false, success) - test.is('data.data should have required property \'test\'', errors[0]) + const result = validation.checkContract(baseContract, extendedSchema) + test.is(false, result.success) + test.is('data.data should have required property \'test\'', result.errors[0]) }) diff --git a/test/validation/validate-overlapping-schema.spec.js b/test/validation/validate-overlapping-schema.spec.js index f8f6bfc..b1942ec 100644 --- a/test/validation/validate-overlapping-schema.spec.js +++ b/test/validation/validate-overlapping-schema.spec.js @@ -51,19 +51,21 @@ const overlappingContract = _.merge({}, baseContract, { ava.test('should validate overlapping contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(overlappingContract, overlappingSchema) ) }) ava.test('Should reject invald overlapping contract', (test) => { - let { success, errors } = validation.checkContract(baseContract, overlappingSchema) - test.is(false, success) - test.is('data.data should have required property \'test\'', errors[0]) + const result = validation.checkContract(baseContract, overlappingSchema) + test.is(false, result.success) + test.is('data.data should have required property \'test\'', result.errors[0]) }) ava.test('Should reject invald overlapping contract', (test) => { - let { success, errors } = validation.checkContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) - test.is(false, success) - test.is('data.data should have required property \'test\'', errors[0]) + const result = validation.checkContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) + test.is(false, result.success) + test.is('data.data should have required property \'test\'', result.errors[0]) }) diff --git a/test/validation/validate-requires.spec.js b/test/validation/validate-requires.spec.js index 0fb8af1..49c5bba 100644 --- a/test/validation/validate-requires.spec.js +++ b/test/validation/validate-requires.spec.js @@ -26,7 +26,9 @@ const requireContract = _.merge({}, baseContract, { requires: [ { or: [ - { type: 'type' }, + { + type: 'type' + }, { or: [ { @@ -41,9 +43,9 @@ const requireContract = _.merge({}, baseContract, { const externalRequireContract = _.merge({}, baseContract, { requires: [ - { data: - { - a: 'a' + { + data: { + prop: 'prop' } } ] @@ -53,11 +55,13 @@ const badExternalRequireContract = _.merge({}, baseContract, { requires: [ { or: [ - { type: 'type' }, + { + type: 'type' + }, { or: [ { - a: 'a' + prop: 'prop' } ] } @@ -70,7 +74,9 @@ const badExternalRequireContract2 = _.merge({}, baseContract, { requires: [ { or: [ - { version: '1' }, + { + version: '1' + } ] } ] @@ -78,24 +84,28 @@ const badExternalRequireContract2 = _.merge({}, baseContract, { ava.test('should validate require contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(requireContract) ) }) ava.test('should validate require contract with unknown fields', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(externalRequireContract) ) }) ava.test('should reject bad require contract with unknown fields', (test) => { - let { success, errors } = validation.checkContract(badExternalRequireContract) - test.is(false, success) + const result = validation.checkContract(badExternalRequireContract) + test.is(false, result.success) }) ava.test('should reject bad require contract that only specifies version', (test) => { - let { success, errors } = validation.checkContract(badExternalRequireContract2) - test.is(false, success) + const result = validation.checkContract(badExternalRequireContract2) + test.is(false, result.success) }) diff --git a/test/validation/validate-self-referencing-contract.spec.js b/test/validation/validate-self-referencing-contract.spec.js index 36e6191..e2a83e4 100644 --- a/test/validation/validate-self-referencing-contract.spec.js +++ b/test/validation/validate-self-referencing-contract.spec.js @@ -41,13 +41,15 @@ const referencingContract = _.merge({}, baseContract, { ava.test('should validate self referencing contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(referencingContract, referencingSchema) ) }) ava.test('Should reject invald self referencing contract', (test) => { - let { success, errors } = validation.checkContract(_.omit(referencingContract, 'data.slug'), referencingSchema) - test.is(false, success) - test.is('data.data should have required property \'.slug\'', errors[0]) + const result = validation.checkContract(_.omit(referencingContract, 'data.slug'), referencingSchema) + test.is(false, result.success) + test.is('data.data should have required property \'.slug\'', result.errors[0]) }) diff --git a/test/validation/validate-tags.spec.js b/test/validation/validate-tags.spec.js index 2ca34a0..48d9366 100644 --- a/test/validation/validate-tags.spec.js +++ b/test/validation/validate-tags.spec.js @@ -28,7 +28,9 @@ const taggedContract = _.merge({}, baseContract, { ava.test('Should validate tagged contract', (test) => { test.deepEqual( - { success: true, errors: [] }, + { + success: true, errors: [] + }, validation.checkContract(taggedContract) ) }) @@ -36,15 +38,15 @@ ava.test('Should validate tagged contract', (test) => { ava.test('Should reject invald tagged contract', (test) => { taggedContract.tags.push('valid') - let { success, errors } = validation.checkContract(taggedContract) - test.is(false, success) - test.is('data.tags should NOT have duplicate items (items ## 1 and 0 are identical)', errors[0]) + const result = validation.checkContract(taggedContract) + test.is(false, result.success) + test.is('data.tags should NOT have duplicate items (items ## 1 and 0 are identical)', result.errors[0]) }) ava.test('Should reject invald tagged contract', (test) => { taggedContract.tags.push(' non valid ') - let { success, errors } = validation.checkContract(taggedContract) - test.is(false, success) - test.is('data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"', errors[0]) + const result = validation.checkContract(taggedContract) + test.is(false, result.success) + test.is('data.tags[2] should match pattern "^[\\S]+(?: [\\S]+)*$"', result.errors[0]) }) From c70ed9999fb7a0226b8dc8910948b98de02a6dd1 Mon Sep 17 00:00:00 2001 From: Giovanni Garufi Date: Thu, 31 May 2018 18:15:23 +0200 Subject: [PATCH 13/13] Fixed according to review --- lib/index.js | 2 +- test/validation/validate-assets.spec.js | 5 +++-- test/validation/validate-base-contracts.spec.js | 5 +++-- test/validation/validate-capabilities.spec.js | 5 +++-- test/validation/validate-children.spec.js | 8 +++++--- test/validation/validate-conflicts.spec.js | 5 +++-- test/validation/validate-extended-schema.spec.js | 7 ++++--- test/validation/validate-overlapping-schema.spec.js | 9 +++++---- test/validation/validate-requires.spec.js | 5 +++-- .../validate-self-referencing-contract.spec.js | 7 ++++--- test/validation/validate-tags.spec.js | 9 +++++---- 11 files changed, 39 insertions(+), 28 deletions(-) diff --git a/lib/index.js b/lib/index.js index 26b29d8..379f710 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,7 +24,7 @@ exports.Contract = require('./contract') exports.Blueprint = require('./blueprint') exports.buildTemplate = require('./partials').buildTemplate -exports.validation = require('./validation') +exports.checkContract = require('./validation').checkContract exports.query = (universe, layout, skeleton) => { const blueprint = new exports.Blueprint(layout, skeleton) diff --git a/test/validation/validate-assets.spec.js b/test/validation/validate-assets.spec.js index 3f75bbc..aa00fd4 100644 --- a/test/validation/validate-assets.spec.js +++ b/test/validation/validate-assets.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,8 @@ const assetsContract = _.merge({}, baseContract, { ava.test('should validate assets contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(assetsContract) ) diff --git a/test/validation/validate-base-contracts.spec.js b/test/validation/validate-base-contracts.spec.js index 146c1b8..eace715 100644 --- a/test/validation/validate-base-contracts.spec.js +++ b/test/validation/validate-base-contracts.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,8 @@ const baseContract = require('./common/baseContract') ava.test('should validate base contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(baseContract) ) diff --git a/test/validation/validate-capabilities.spec.js b/test/validation/validate-capabilities.spec.js index def6e3e..83df8fb 100644 --- a/test/validation/validate-capabilities.spec.js +++ b/test/validation/validate-capabilities.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,8 @@ const badCapabilitiesContract = _.merge({}, baseContract, { ava.test('should validate capabilities contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(capabilitiesContract) ) diff --git a/test/validation/validate-children.spec.js b/test/validation/validate-children.spec.js index 6348cf8..2eef409 100644 --- a/test/validation/validate-children.spec.js +++ b/test/validation/validate-children.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,7 +50,8 @@ const badFamilyContract = _.merge({}, baseContract, { ava.test('should validate father contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(fatherContract) ) @@ -59,7 +60,8 @@ ava.test('should validate father contract', (test) => { ava.test('should validate grampa contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(grampaContract) ) diff --git a/test/validation/validate-conflicts.spec.js b/test/validation/validate-conflicts.spec.js index fa691d5..b37642f 100644 --- a/test/validation/validate-conflicts.spec.js +++ b/test/validation/validate-conflicts.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,8 @@ const badConflictsContract = _.merge({}, baseContract, { ava.test('should validate conflicts contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(conflictsContract) ) diff --git a/test/validation/validate-extended-schema.spec.js b/test/validation/validate-extended-schema.spec.js index a8f304b..3738ae7 100644 --- a/test/validation/validate-extended-schema.spec.js +++ b/test/validation/validate-extended-schema.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,13 +52,14 @@ const extendedContract = _.merge({}, baseContract, { ava.test('should validate extended contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(extendedContract, extendedSchema) ) }) -ava.test('Should reject invald extended contract', (test) => { +ava.test('Should reject invalid extended contract', (test) => { const result = validation.checkContract(baseContract, extendedSchema) test.is(false, result.success) test.is('data.data should have required property \'test\'', result.errors[0]) diff --git a/test/validation/validate-overlapping-schema.spec.js b/test/validation/validate-overlapping-schema.spec.js index b1942ec..fef682a 100644 --- a/test/validation/validate-overlapping-schema.spec.js +++ b/test/validation/validate-overlapping-schema.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,19 +52,20 @@ const overlappingContract = _.merge({}, baseContract, { ava.test('should validate overlapping contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(overlappingContract, overlappingSchema) ) }) -ava.test('Should reject invald overlapping contract', (test) => { +ava.test('Should reject invalid overlapping contract', (test) => { const result = validation.checkContract(baseContract, overlappingSchema) test.is(false, result.success) test.is('data.data should have required property \'test\'', result.errors[0]) }) -ava.test('Should reject invald overlapping contract', (test) => { +ava.test('Should reject invalid overlapping contract', (test) => { const result = validation.checkContract(_.omit(overlappingContract, 'data.test'), overlappingSchema) test.is(false, result.success) test.is('data.data should have required property \'test\'', result.errors[0]) diff --git a/test/validation/validate-requires.spec.js b/test/validation/validate-requires.spec.js index 49c5bba..54b3e10 100644 --- a/test/validation/validate-requires.spec.js +++ b/test/validation/validate-requires.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -85,7 +85,8 @@ const badExternalRequireContract2 = _.merge({}, baseContract, { ava.test('should validate require contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(requireContract) ) diff --git a/test/validation/validate-self-referencing-contract.spec.js b/test/validation/validate-self-referencing-contract.spec.js index e2a83e4..1e2c181 100644 --- a/test/validation/validate-self-referencing-contract.spec.js +++ b/test/validation/validate-self-referencing-contract.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,13 +42,14 @@ const referencingContract = _.merge({}, baseContract, { ava.test('should validate self referencing contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(referencingContract, referencingSchema) ) }) -ava.test('Should reject invald self referencing contract', (test) => { +ava.test('Should reject invalid self referencing contract', (test) => { const result = validation.checkContract(_.omit(referencingContract, 'data.slug'), referencingSchema) test.is(false, result.success) test.is('data.data should have required property \'.slug\'', result.errors[0]) diff --git a/test/validation/validate-tags.spec.js b/test/validation/validate-tags.spec.js index 48d9366..91010f7 100644 --- a/test/validation/validate-tags.spec.js +++ b/test/validation/validate-tags.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2017 resin.io + * Copyright 2018 resin.io * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,13 +29,14 @@ const taggedContract = _.merge({}, baseContract, { ava.test('Should validate tagged contract', (test) => { test.deepEqual( { - success: true, errors: [] + success: true, + errors: [] }, validation.checkContract(taggedContract) ) }) -ava.test('Should reject invald tagged contract', (test) => { +ava.test('Should reject invalid tagged contract', (test) => { taggedContract.tags.push('valid') const result = validation.checkContract(taggedContract) @@ -43,7 +44,7 @@ ava.test('Should reject invald tagged contract', (test) => { test.is('data.tags should NOT have duplicate items (items ## 1 and 0 are identical)', result.errors[0]) }) -ava.test('Should reject invald tagged contract', (test) => { +ava.test('Should reject invalid tagged contract', (test) => { taggedContract.tags.push(' non valid ') const result = validation.checkContract(taggedContract)